summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDidier Raboud <odyx@debian.org>2018-01-07 12:32:11 +0100
committerDidier Raboud <odyx@debian.org>2018-01-07 12:32:11 +0100
commit97c310418e4266fd2a69633786e2b09e03584ed9 (patch)
tree79a19de13087c8bcda1057301af1c81de9bdfa9e
Import usb-modeswitch_2.5.2+repack0.orig.tar.xz
[dgit import orig usb-modeswitch_2.5.2+repack0.orig.tar.xz]
-rw-r--r--COPYING340
-rw-r--r--ChangeLog321
-rw-r--r--Makefile105
-rw-r--r--README279
-rw-r--r--dispatcher.c88
-rwxr-xr-xmake_string.tcl31
-rw-r--r--usb-modeswitch-upstart.conf5
-rw-r--r--usb_modeswitch.1175
-rw-r--r--usb_modeswitch.c2115
-rw-r--r--usb_modeswitch.conf40
-rw-r--r--usb_modeswitch.h116
-rwxr-xr-xusb_modeswitch.sh75
-rwxr-xr-xusb_modeswitch.tcl1005
-rw-r--r--usb_modeswitch@.service8
-rw-r--r--usb_modeswitch_dispatcher.121
15 files changed, 4724 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..066d38a
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,321 @@
+
+History of USB_ModeSwitch
+=========================
+
+Version 2.5.2, 2017/12/31
+ Bugfix release: fixed additional MessageContent parameters not working,
+ reported by Frank Schmirler (see
+ http://draisberghof.de/usb_modeswitch/bb/viewtopic.php?p=18369 );
+ fixed "early crash" when killed before libusb context initialization
+ (see https://bugzilla.redhat.com/show_bug.cgi?id=1358472 ), reported
+ by Lubomir Rintel;
+ fixed bad string reference, reported by Lubomir Rintel, (see
+ http://draisberghof.de/usb_modeswitch/bb/viewtopic.php?p=18238 );
+ fixed detection of systemd if /sbin/init is a cascaded symlink
+ (see http://draisberghof.de/usb_modeswitch/bb/viewtopic.php?p=18218 );
+ fixed quirk in help message from usb_modeswitch binary
+Version 2.5.1, 2017/08/06
+ New parameter "HuaweiAltMode", uses an alternative Huawei standard bulk
+ message which will either provide NCM (newer modems) or plain PPP ports
+ (older modems); new option in the global configuration file
+ "HuaweiAltModeGlobal", allowing easy override of "HuwaeiNewMode" with
+ "HuaweiAltMode"; reworked USB configuration switching, configuring a
+ device reset first before setting the target configuration; improved
+ driver detachment, now taking all configured interfaces into account
+Version 2.5.0, 2017/01/17
+ ATTENTION: Parameter transmitted from udev now reduced to %k (kernel
+ name), however, rules file parameters can be '%b/%k' or '%k' - this
+ fixes issues with parameter handling by the systemd unit file;
+ remove endpoint reset ahead of bulk message transmission, only reset
+ if endpoints are actually stalled, helping with quirky device behaviour
+ (see www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?f=2&t=2582 );
+ stability fix by making a buffer in the config parser static, preventing
+ problems seen in Fedora (thanks to Lubomir Rintel for patches and hints
+ regarding the three previous issues); removed premature driver unbinding
+ in wrapper which could disrupt certain devices already in target mode
+ (reported by Aleksander Morgado); improved check for determining install
+ mode (essential with generic config files present, e.g. 12d1:#linux)
+Version 2.4.0, 2016/06/12
+ ATTENTION: All ad-hoc driver binding code (using new_id driver attribute)
+ removed - was a potential source of side effects and should now be
+ obsoleted by good kernel support for modems; Added "dummy" setting for
+ config files, to conditionally refrain from handling a device (see this
+ topic: www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?f=4&t=2458 );
+ extended StandardEject sequence to include LUN 1, required for some D-Link
+ devices; add device class 239 ("miscellaneous") to sanity check (thanks to
+ Daniel Drake for reporting); udev shell script - removed driver binding,
+ removed almost all waiting and forking, improved check for systemd (thanks
+ to Daniel Drake for problem analysis and solution, see this topic/patch:
+ www.draisberghof.de/usb_modeswitch/bb/viewtopic.php?p=16777#p16777 ,
+ gist.github.com/dsd/9f83c4830ab78ce94078aedb2cf16a8f )
+Version 2.3.0, 2016/01/12
+ ATTENTION: -I flag is now history and being ignored - determining SCSI
+ attributes is really an 'outside task'; -n flag (NeedResponse) is being
+ ignored, CSW response will now always be read; introduction of parameter
+ "OptionMode", wrapping the standard bulk message for all newer Huawei
+ devices; fixed missing variable initialization in dispatcher script which
+ could lead to crash (thanks, Dmitry Kunilov!); fixed bug which prevented
+ early logging; fixed success report for Cisco AM10; some source code
+ formatting and clean-up
+Version 2.2.6, 2015/11/01
+ Renamed function abort(), avoiding possible conflicts in static builds
+ with libjim (thanks, Gustavo Zacharias); removed storage class check of
+ interface 0 from dispatcher, enabling new multi-config devices in data
+ package 20151101
+Version 2.2.5, 2015/07/16
+ Fixed bug in configuration check, possibly leading to segfault (thanks,
+ Leonid Lisovskiy); fixed Pantech commandline parameter evaluation (was
+ not working at all); added driver unbind step via sysfs in wrapper,
+ getting rid of the USB subsystem complaint "interface 0 claimed by
+ usb-storage while 'usb_modeswitch' <does this and that>"
+Version 2.2.4, 2015/07/14
+ Fixed buggy check of USB configuration selection (possibly leading
+ to segfault), tested with Alcatel X602D; removed call to 'libusb_strerror'
+ from libusb initialization - not available in earlier libusb1 versions
+Version 2.2.3, 2015/06/29
+ Fixed problem arising with systemd version 221 (220 untested), which
+ affects starting the usb_modeswitch systemd unit from the sh script
+ (reported by Archlinux users)
+Version 2.2.2, 2015/06/27
+ Added catch for libusb init error (thanks, Henrik Gustafsson); removed
+ global function result variable; added catch for USB configuration
+ read error (both thanks to "otila"); fixed wrapper script where port
+ search for symlinking modem port was broken ("/dev/gsmmodem"); changed
+ PantechMode parameter to represent different targets; added global
+ config option to disable MBIM checking and setting alltogether (request
+ from "kai"); changed udev sh script so that systemd processing takes
+ precedence over upstart; changed systemd template unit parameter to
+ avoid escaping problems
+Version 2.2.1, 2015/01/15
+ Fixed unreliable switching function for Cisco AM10
+Version 2.2.0, 2014/05/29
+ Introduction of parameter "HuaweiNewMode", wrapping the standard bulk
+ message for all newer Huawei devices; support for generic fall-back
+ config files, combined with OS switch (per vendor ID), implementation
+ to use a specific switching command on Android for all Huawei devices
+ (see README of data package for details); this change was suggested
+ by Huawei
+Version 2.1.1, 2014/03/27
+ Code cleanup, better use of libusb1; this also fixes problems with
+ determination of the active USB configuration (Samsung, Option modems);
+ "Interface" parameter was not working as expected, fixed (see also:
+ https://bugs.launchpad.net/bugs/1261923 ); fixed bogus interface release
+ in "inquire" function, again a report by "Sonya@zte"
+Version 2.1.0, 2014/01/28
+ ATTENTION: -I flag meaning reversed, default is to skip SCSI inquiry;
+ introduction of StandardEject, replacing many MessageContents with the
+ same function, reducing size of device config files, and always including
+ the 'Allow Medium Removal' before ejecting (thanks to Lars Melin for
+ the idea); fix in "bulk_read", removing bogus CSW request (report from
+ "Sonya@zte")
+Version 2.0.1, 2013/09/03
+ Fixed stupid string size bug which could lead to memory corruption
+ (thanks to Leonid Myravjev)
+Version 2.0.0, 2013/09/01
+ Switched to libusb1.0, with much help from folks of the wl500g project
+ (http://wl500g.googlecode.com): Vladislav Grishenko, Leonid Lisovskiy,
+ Roman Samarev, Andrey Tikhomirov;
+ major code and debug output cleanup; man page corrections and additions
+ (thanks to Thomas Haller);
+ experimental systemd and upstart integration, if present
+ (Explanation: newer udev versions kill all subprocesses, detached or
+ not. The suggested way to handle longer running processes like the
+ usb_modeswitch_dispatcher is to add simple services or tasks and start
+ these by sending signals from the udev rule)
+ !! Attention, system integrators: a crude install facility is included
+ in the Makefile to check if "upstart" or "systemd" is active and to
+ install the matching service file; you may want to adapt it better to
+ your respective system, possibly adding dependencies/targets to the
+ services. Note that the udev starter script usb_modeswitch.sh also
+ checks for the existence of the service/unit files
+Version 1.2.7, 2013/08/07
+ Two new dedicated control message functions to support Pantech LTE
+ (thanks to Adam Goode) and Blackberry Q10/Z10 (thanks to Daniel Mende)
+Version 1.2.6, 2013/06/02
+ Several changes to streamline compiling as part of larger projects
+ (thanks to Nicolas Carrier), mostly in Makefile; fix compiler warnings
+ appearing in certain build environments (N. Carrier); new Quanta
+ procedure (thanks to Andrey Tikhomirov) for Quanta 1K3 LTE; fix for
+ error with cascaded hubs in dispatcher script (hint from N. Carrier)
+Version 1.2.5, 2012/11/09
+ Initial support for MBIM devices, use with data package >= 20121109;
+ checking for these is the automatic default, new parameter NoMBIMCheck
+ prevents the check per device in case of problems; new global option
+ to set "delay_use" of usb-storage (as low values may prevent
+ mode-switching); fix for handling multi-configuration devices (thanks
+ to Bjørn Mork for advice)
+Version 1.2.4, 2012/08/12
+ Additional interface checks to prevent sending UFI commands to non-
+ storage interfaces (prompted by more ambiguous device IDs popping up);
+ change in SierraMode for handling newer devices which caused an error
+ abort before; Makefile fix for parallelized make runs
+Version 1.2.3, 2012/01/28
+ Fixed two bugs both causing the embedded-jimsh install variant of the
+ dispatcher crash (the "pure-script" install variant was NOT affected);
+ fixed some "regexp" incompatibilities with Debian's libjim
+Version 1.2.2, 2012/01/19
+ Fixed bad bug preventing mode switch for devices using TargetClass;
+ improved logging in case of negative success check
+Version 1.2.1, 2011/12/26
+ Fixed possible ambiguities when multiple identical modems are plugged
+ synchronously; this is achieved by adding -b and -g parameter (busnum
+ and devnum) for proper id'ing; some resulting workflow changes and
+ shortcuts in "system integration" mode (use sysfs/wrapper for success
+ control, reduce informative checks); improved hub usage robustness;
+ future data packages can be ridded of redundancies (default ID, success
+ check parameters), resulting in smaller files;
+ fixed bad bug which may prevent switching completely when logging is
+ not activated; fixed possible overflow in dispatcher C code (thanks to
+ Gilles Espinasse)
+Version 1.2.0, 2011/10/23
+ Added QisdaMode for Qisda H21 (thanks to Chi-Hang Long for the code);
+ dispatcher can now be installed with an embedded interpreter, so that
+ Tcl is no longer required; added command line options for binary program
+ to accept configuration data via stdin or as a long string parameter -
+ this fixes the bug with non-writable temporary file during boot;
+ reversed skipping of pre-switching delay, which has caused problems;
+ fixed potential buffer overflow (thanks to Rafael Silva for the find);
+ get first interface right even on some broken devices (thanks to
+ Alexander Gordeev for the patch); increased post-switch delay before
+ driver binding to avoid possible conflict with usb-storage
+Version 1.1.9, 2011/08/05
+ Added CiscoMode for Valet device; additional checking for CDC ACM device
+ to prevent erroneous driver loading after switching; no more post-switch
+ check for access to initial device if target parameters are given
+Version 1.1.8, 2011/06/19
+ Cleaned up switchSendMessage(); added workaround for quirky devices not
+ reporting configuration setting; added non-CSW response for arbitrary
+ bulk transfers; added SequansMode and MobileActionMode; check for active
+ configuration will be skipped if bNumConfigurations is 1 (most cases)
+Version 1.1.7, 2011/02/27
+ Attention: paths for runtime files and database have changed! Old places
+ will be found but are deprecated; /usr/share/usb_modeswitch for database,
+ /var/lib/usb_modeswitch for "remembered" IDs;
+ fix for configuration setting race (thanks to Amit Mendapara); discovered
+ incompatibility between Tcl versions <= 8.3 and >=8.4, so 8.4 is the
+ minimum prerequisite now;
+ first availability of an alternative source pack which includes "jimsh",
+ a fast Tcl mini shell, intended for resource-constrained systems
+Version 1.1.6, 2010/12/22
+ Moved warm-boot driver binding to sh wrapper, was unreliable in 1.1.5;
+ sh wrapper overhaul, made compatible with Ubuntu's "dash" shell, tclsh
+ calls reduced further; initial device checking includes current
+ bConfigurationValue now, should work with config setting for multiple
+ devices; made tcl script conform to limitations of "jimsh", the minimal
+ tcl shell (hint from Barry Kauler); fixes for "usbserial" fallback
+ (driver binding for old systems); in the C program, changed parameter
+ "MessageDelay" (hitherto unused) to "ReleaseDelay", to be used in one
+ device configuration (delay interface release after bulk message sending)
+Version 1.1.5, 2010/11/28
+ Added special control message for Kobil devices (patch from Filip Aben);
+ try to get active configuration for interface class checking (there are
+ some new devices 'switching' via configuration selection);
+ fixed "0000" target product ID - again; new bash and tcl wrapper logic:
+ the convenience functions for driver binding and symlinking will now
+ start the tcl shell ONLY for known devices; changed and appended logging
+ capabilities of said convenience functions; add loading of "usbserial"
+ as a fallback for older systems to support new devices;
+ add workaround for bug in libusb1 which affects device search during
+ success check
+Version 1.1.4, 2010/08/17
+ The package should work at boot time now (cold and warm);
+ product IDs of "0000" do exist but were not accepted, fixed (thanks to
+ Sakis Dimopoulos); response endpoint is now always detected (led to
+ possible error report when resetting all endpoints in version 1.1.3);
+ wrapper script can now work with a packed collection of config files as
+ well as with the plain folder of files; use with the "install-packed"
+ make target of the data package (for use on systems with resource
+ constraints); wrapper fix for the symlink feature: handling of multiple
+ interrupt ports was incomplete; wrapper does not longer use a temporary
+ file for the symlink feature (security considerations, Marco d'Itri)
+Version 1.1.3, 2010/06/21
+ Added delay option to separate multiple message transfers by millisecs;
+ fixed (possibly dangerous) sloppy string handling (thanks to Christophe
+ Fergeau); added "clear_halt" for response endpoint; small additions in
+ Makefile (install with -D); changes in option handling (NO MORE DEFAULT
+ CONFIG FILE!) and help text; symlink feature in wrapper can now cope
+ with devices providing more than one interrupt port; wrapper now ignores
+ package manager leftovers in config folder; replaced bash-specific syntax
+ in wrapper; changed ZTE skipping (if existing rules are found) to warning
+Version 1.1.2, 2010/04/18
+ Added support for two additional bulk messages; wrapper handles special
+ ZTE case; generalized driver loading, new parameter "DriverModule" and
+ "DriverIDPath"; new wrapper facility to add symlink pointing to interrupt
+ port (used in rule file from data pack >= 20100418)
+Version 1.1.1, 2010/03/17
+ Attention: old usb_modeswitch.conf renamed to usb_modeswitch.setup!
+ Add separate config file for wrapper (global settings for switching and
+ logging); add config file option to disable driver loading; handling of
+ kernel attribute AVOID_RESET_QUIRK added; bug fixed in SonyMode (reported
+ by "no-0n3"); bug fixed in SuccessCheck logic; minor flow alignments and
+ fixes; new devices
+Version 1.1.0, 2010/01/24
+ Attention: wrapper script location changed, uninstall old versions!
+ Major fixes in the wrapper script (stabilizing and time-saving);
+ back to unified installation, defaults to "integrated" approach;
+ new -D parameter to enable "integrated" behaviour; bugs fixed in
+ success check; man file included (borrowed from the Debian package);
+ C code and binary works with the compat library from libusb-1.0;
+ some new devices
+Version 1.0.7, 2010/01/06
+ Bug fixed for Sony mode, thanks to Marco Chiaranda; fix for parameter
+ substitution in newer udev versions, fix for bad bug in wrapper script
+ practically disabling automatic mode
+Version 1.0.6, 2009/12/21
+ New "GCT Mode", fixes for device quirks (NXP Dragonfly), fix for multiple
+ Huawei devices, cleanups, loads of new devices in config file and database,
+ minor tcl script changes
+Version 1.0.5, 2009/08/26
+ More changes and fixes regarding success check; "--version" option;
+ config "database" updated
+Version 1.0.4, 2009/08/23
+ Success check bugs (and others) fixed
+Version 1.0.3, 2009/08/20
+ Success check improved; experimental system integration (fully automated),
+ optional; new parameter "TargetProductList" needed for this; other
+ necessary small adaptations; more devices
+Version 1.0.2, 2009/06/10
+ Output bugs fixed
+Version 1.0.1, 2009/06/08
+ Added output of descriptor strings for further identification
+Version 1.0.0, 2009/06/01
+ Attention: possible incompatibilities for command line control!
+ On/off flags don't require arguments anymore (-H, -S, -O, -d, -R,
+ -n, new: -I), meaning "-R 0" does a reset like "-R 1" or "-R";
+ long option names changed to standard format (e.g. --HuaweiMode to
+ --huawei-mode); added device inquiry, for future help with device
+ identification; catch error -19 as possible success; send and response
+ endpoints now autoselected (consequently NeedResponse is back);
+ new devices
+Version 0.9.7, 2009/04/15
+ Updated SonyMode, MD 400 now stable; automatic default endpoint
+ detection from Andrew Bird
+Version 0.9.7beta, 2009/03/15
+ Major code clean up, optional success control (both suggested
+ by Daniel Cooper), new devices
+Version 0.9.6, 2009/01/08
+ Special modes added for Sierra and Sony Ericsson, new devices
+Version 0.9.5, 2008/10/27
+ New options for USB tuning (jokedst), lots of new devices, clean up
+Version 0.9.4, 2008/06/09
+ Compat fix for libusb on FreeBSD quirks, more devices
+Version 0.9.4beta2, 2008/03/19
+ Successful udev device release fix
+Version 0.9.4beta, 2008/03/16
+ Multiple device support
+Version 0.9.3, 2008/03/09
+ More devices, no changes from beta version
+Version 0.9.3beta, 2007/12/30
+ New TargetClass parameter for recent Option firmware (Paul Hardwick), more
+ devices
+Version 0.9.2, 2007/11/02
+ New Huawei mode (code from Miroslav Bobovsky, added by Denis Sutter), more
+ devices
+Version 0.9.1beta, 2007/09/04 (jokedst)
+ Added command line parsing, cleaned up config stuff, doc updates
+Version 0.9beta, 2007/08/15
+ Name change from "icon_switch", parameter file and generalizing
+Version 0.2, 2006/09/25
+ Code cleaning, more messages
+Version 0.1, 2006/09/24
+ (as "icon_switch") Just very basic functionality ...
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..463a11f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,105 @@
+PROG = usb_modeswitch
+VERS = 2.5.2
+CC ?= gcc
+CFLAGS += -Wall
+LIBS = `pkg-config --libs --cflags libusb-1.0`
+RM = /bin/rm -f
+OBJS = usb_modeswitch.c
+PREFIX = $(DESTDIR)/usr
+ETCDIR = $(DESTDIR)/etc
+SYSDIR = $(ETCDIR)/systemd/system
+UPSDIR = $(ETCDIR)/init
+UDEVDIR = $(DESTDIR)/lib/udev
+SBINDIR = $(PREFIX)/sbin
+MANDIR = $(PREFIX)/share/man/man1
+VPATH = jimtcl
+HOST_TCL := $(shell cd jim && ./autosetup/find-tclsh)
+ifeq (,$(findstring jimsh0,$(HOST_TCL)))
+TCL ?= $(HOST_TCL)
+else
+TCL ?= /usr/bin/tclsh
+endif
+JIM_CONFIGURE_OPTS = --disable-lineedit \
+ --with-out-jim-ext="stdlib posix load signal syslog" --prefix=/usr
+
+.PHONY: clean install install-common uninstall \
+ script shared static \
+ dispatcher-script dispatcher-shared dispatcher-static \
+ install-script install-shared install-static
+
+all: script
+
+script: $(PROG) dispatcher-script
+
+shared: $(PROG) dispatcher-shared
+
+static: $(PROG) dispatcher-static
+
+$(PROG): $(OBJS) usb_modeswitch.h
+ $(CC) -o $(PROG) $(OBJS) $(CFLAGS) $(LIBS) $(LDFLAGS)
+
+jim/libjim.so:
+ cd jim && CFLAGS="$(CFLAGS)" CC="$(CC)" ./configure $(JIM_CONFIGURE_OPTS) --shared
+ $(MAKE) -C jim lib
+
+jim/libjim.a:
+ cd jim && CFLAGS="$(CFLAGS)" CC="$(CC)" ./configure $(JIM_CONFIGURE_OPTS)
+ $(MAKE) -C jim lib
+
+dispatcher-script: usb_modeswitch.tcl
+ sed 's_!/usr/bin/tclsh_!'"$(TCL)"'_' < usb_modeswitch.tcl > usb_modeswitch_dispatcher
+
+dispatcher-shared: jim/libjim.so dispatcher.c usb_modeswitch.string
+ $(CC) dispatcher.c $(LDFLAGS) -Ljim -ljim -Ijim -o usb_modeswitch_dispatcher $(CFLAGS)
+
+dispatcher-static: jim/libjim.a dispatcher.c usb_modeswitch.string
+ $(CC) dispatcher.c $(LDFLAGS) jim/libjim.a -Ijim -o usb_modeswitch_dispatcher $(CFLAGS)
+
+usb_modeswitch.string: usb_modeswitch.tcl
+ $(HOST_TCL) make_string.tcl usb_modeswitch.tcl > $@
+
+clean:
+ $(RM) usb_modeswitch
+ $(RM) usb_modeswitch_dispatcher
+ $(RM) usb_modeswitch.string
+ $(RM) jim/autosetup/jimsh0
+ $(RM) jim/autosetup/jimsh0.c
+
+distclean: clean
+ -$(MAKE) -C jim distclean
+
+ums-clean:
+ $(RM) usb_modeswitch
+ $(RM) usb_modeswitch_dispatcher
+ $(RM) usb_modeswitch.string
+
+# If the systemd folder is present, install the service for starting the dispatcher
+# If not, use the dispatcher directly from the udev rule as in previous versions
+
+install-common: $(PROG) usb_modeswitch_dispatcher
+ install -D --mode=755 usb_modeswitch $(SBINDIR)/usb_modeswitch
+ install -D --mode=755 usb_modeswitch.sh $(UDEVDIR)/usb_modeswitch
+ install -D --mode=644 usb_modeswitch.conf $(ETCDIR)/usb_modeswitch.conf
+ install -D --mode=644 usb_modeswitch.1 $(MANDIR)/usb_modeswitch.1
+ install -D --mode=644 usb_modeswitch_dispatcher.1 $(MANDIR)/usb_modeswitch_dispatcher.1
+ install -D --mode=755 usb_modeswitch_dispatcher $(SBINDIR)/usb_modeswitch_dispatcher
+ install -d $(DESTDIR)/var/lib/usb_modeswitch
+ test -d $(UPSDIR) -a -e /sbin/initctl && install --mode=644 usb-modeswitch-upstart.conf $(UPSDIR) || test 1
+ test -d $(SYSDIR) -a \( -e /usr/bin/systemctl -o -e /bin/systemctl \) && install --mode=644 usb_modeswitch@.service $(SYSDIR) || test 1
+
+install: install-script
+
+install-script: dispatcher-script install-common
+
+install-shared: dispatcher-shared install-common
+
+install-static: dispatcher-static install-common
+
+uninstall:
+ $(RM) $(SBINDIR)/usb_modeswitch
+ $(RM) $(SBINDIR)/usb_modeswitch_dispatcher
+ $(RM) $(UDEVDIR)/usb_modeswitch
+ $(RM) $(ETCDIR)/usb_modeswitch.conf
+ $(RM) $(MANDIR)/usb_modeswitch.1
+ $(RM) -R $(DESTDIR)/var/lib/usb_modeswitch
+ $(RM) $(SYSDIR)/usb_modeswitch@.service
diff --git a/README b/README
new file mode 100644
index 0000000..a066910
--- /dev/null
+++ b/README
@@ -0,0 +1,279 @@
+README for USB_ModeSwitch
+
+For up-to-date and more detailed information (plus a friendly forum) visit
+http://www.draisberghof.de/usb_modeswitch
+
+
+What it is
+==========
+
+USB_ModeSwitch is - hardly surprising - a mode switching tool for controlling
+USB devices with multiple "modes". Now, what does THAT mean?
+
+More and more USB devices have their MS Windows drivers onboard; when plugged
+in for the first time they act like a flash storage and offer their driver
+installation from there.
+After installation (and on every consecutive plugging) the driver switches the
+mode internally by sending a certain command sequence; the storage device
+vanishes (in most cases) and a different device - like a USB modem - shows up.
+To the host, this is like unplugging one device and then plugging annother.
+
+At first this feature appeared on devices with cell phone chipsets, presumably
+because some of them were already able to change the mode of their USB port
+from storage to communication - so why not make use of this in a modem stick?
+Modem maker "Option" calls that feature "ZeroCD (TM)" since it eliminates the
+need for shipping a separate driver carrier.
+
+In the beginning, nothing of this was documented in any form and there was
+hardly any Linux/Unix support available.
+On the good side, most of the known devices are working out of the box in all
+modes with available Linux modules like "usb-storage" or serial USB drivers.
+That leaves only the problem of the mode-switching from storage to whatever
+the thing is supposed to do.
+
+Fortunately there are things like human smartness, USB sniffing programs and
+LibUSB. The obvious way is to eavesdrop on the communication of the MS Windows
+driver, to isolate the command or action that does the switching, and then re-
+play the same sequence in a non-Windows system.
+
+In theory, this task could also be handled on the kernel driver level, but a
+userspace program is much more flexible and can easily be disabled if access
+to the initial mode of those devices should be desired. There has been a
+principle decision by kernel developers to keep mode-switching outside of the
+kernel.
+
+So USB_ModeSwitch has evolved to make this process easy to handle by taking the
+relevant parameters from configuration files and handling all initialization
+and communication business, with essential help from "libusb".
+
+In Linux and friends it is intended to work automatically - via udev events
+and rules - and doing the mode switch without any user interaction.
+However, the core C program should be as portable als libusb itself; it does not
+rely on specific Linux features.
+It can also be run as an interactive command line tool, which can be useful
+when trying to tinker with hitherto unknown devices.
+
+We have already collected a wide range of information on how to mode-switch all
+sorts of devices. If you run into a new one that is unknown yet, don't despair:
+we can find out what you need to do!
+
+
+How to install
+==============
+
+If you only need the core C program, just run "make". All further steps de-
+scribed below are referring to a common, fairly current Linux system where
+USB_ModeSwitch is expected to do its work automatically.
+
+!! You need the usb-modeswitch-data package from the same source as this !!
+
+If you have an earlier version installed, de-installation is recommended ("make
+uninstall") but not mandatory. The wrapper script location changed in 1.1.0;
+old files might be orphaned but will not do any harm.
+
+The main prerequisite for installing from source is the development package for
+"libusb". It may be called "libusb-dev" or similar in your distribution. From
+usb_modeswitch 2.0.0 on, it should have an "1.x" in the name to reflect the change
+to libusb-1. There are also variants around called "libusbx" if libusb-1 is not
+available on your distribution.
+
+To install the tool set, unpack and run the install command (see below) in the
+newly created directory.
+
+From version 1.2.0, there are three flavours of installing. The only difference
+between those is the way the dispatcher is installed, but this affects the
+dependencies as well:
+
+1. If you have the "Tcl" scripting language available on your system (packages
+ "tcl" or "jimsh"), use the light-weight installation:
+
+ # make install
+
+2. If you are size-constrained and have the Jimsh library on your system
+ (package "libjim-dev"), you can have the Tcl interpreter embedded with the
+ dispatcher, using its shared lib:
+
+ # make install-shared
+
+3. If you are size-constrained and definitely don't need a Tcl interpreter
+ for anything else, choose the statically embedded flavour. This will have
+ no further dependency as it uses the included interpreter code, which is
+ configured for small size:
+
+ # make install-static
+
+Note that the "static"/"shared" targets are NOT referring to the usb_modeswitch
+program, only to the dispatcher!
+Any one of these commands will install a small posix shell script, the
+dispatcher (wrapper) as script or as binary, a global config file, the core
+program and a man page.
+
+Install the data package as well and you are set.
+
+NOTE: installing over (possibly outdated) distribution packages of this
+program and the data collection should generally not be a problem.
+
+
+How to use
+==========
+
+If your device is known, you should be able to just plug it and use it. If
+it doesn't work - we will find out why.
+
+For manual use just run "usb_modeswitch" (as root). Work with the command
+line interface or with a setup file. You can use any file and give its path
+with the "-c" parameter.
+
+The file named "device_reference.txt" that you can find on the home site of
+this package is a device and configuration reference containing most known
+devices; you can use it as a "database" to create your own configuration file.
+It's heavily commented and should tell you what to do. It also contains a
+thorough explanation of all the parameters.
+However, the device list in that reference file is no longer maintained.
+See the data package for the USB IDs of devices known to usb_modeswitch.
+
+Run "usb_modeswitch -h" to list the command line parameters.
+See also the provided man page.
+
+Important note: Manual use is mainly intended for testing and analyzing!!
+The program puts no limits on what you can send to your USB device, so I
+assume it is possible to screw it up profoundly. You have been warned.
+
+Once your new device is switching fine you can add it to the data files to
+make the process automatic.
+
+For this to work, add a rule entry to the rules file to let udev run
+usb_modeswitch as soon as the default IDs are found (when the device is
+plugged). If you look into the rules file you will see immediately how
+your new entry should look like.
+The location is:
+/lib/udev/rules.d/40-usb_modeswitch.rules
+
+Then add your new config file to the folder
+"/etc/usb_modeswitch.d" (only for custom config files!).
+And don't forget to report your success !!
+
+Again, remember that the rules file and the default device config folder
+(/usr/share/usb_modeswitch) are installed by the usb_modeswitch data package.
+
+
+##########
+Important: libusb programs - like this tool - want to be run with administrative
+privileges (as root or with sudo)!
+##########
+
+
+
+Known working hardware, Troubleshooting
+=======================================
+
+Please go to the homepage (see link at the top). Read carefully.
+For support questions use ONLY public posts in the forum.
+
+ONCE AGAIN: The USB IDs of devices are the only halfway reliable indicator to
+determine if they are known/supported. Brand and model names are often not
+helpful, due to rebranding and fimrware variations.
+See the data package, folder "usb_modeswitch.d" for known IDs.
+
+
+
+Contribute
+==========
+
+USB_ModeSwitch comes quite handy for experimenting with your own hardware if
+not supported yet. You could try this approach:
+
+Note the device's vendor and product ID from /proc/bus/usb/devices (or from the
+output of lsusb); the assigned driver is usually "usb-storage". Then try spying
+on the USB communication to the device with the same ID inside MS Windoze.
+Nowadays the standard tool for this is Wireshark/USBPcap.
+
+PLEASE post any improvements, new device information and/or bug reports to the
+forum (see above) or send it to the author (see below)!
+
+
+Whodunit
+========
+
+Copyright 2007 - 2017 Josua Dietze (for non-support notifications write a personal
+message through the forum to "Josh"; everything else only in a forum thread)
+
+ !!! NO SUPPORT QUESTIONS VIA E-MAIL, use the forum !!!
+
+Major contributions:
+
+Command line parsing and other essential contributions:
+ Joakim Wennergren
+
+TargetClass parameter implementation to support new Option devices/firmware:
+ Paul Hardwick (http://www.pharscape.org)
+
+Created with initial help from:
+ "usbsnoop2libusb.pl" by Timo Lindfors
+ (http://iki.fi/lindi/usb/usbsnoop2libusb.pl)
+
+Config file parsing code borrowed from:
+ Guillaume Dargaud (http://www.gdargaud.net/Hack/SourceCode.html)
+
+Hexstr2bin function borrowed from:
+ Jouni Malinen (http://hostap.epitest.fi/wpa_supplicant, from "common.c")
+
+Indispensable help with device research and compilation:
+ Lars Melin
+
+Code, fixes and ideas contributed by:
+ Aki Makkonen
+ Denis Sutter
+ Lucas Benedicic
+ Roman Laube
+ Luigi Iotti
+ Vincent Teoh
+ Tommy Cheng
+ Daniel Cooper
+ Andrew Bird
+ Yaroslav Levandovskiy
+ Sakis Dimopoulos
+ Steven Fernandez
+ Christophe Fergeau
+ Nils Radtke
+ Filip Aben
+ Amit Mendapara
+ Roman S. Samarev
+ Chi-Hang Long
+ Andrey Tikhomirov
+ Daniel Mende
+ Nicholas Carrier
+ Adam Goode
+ Leonid Lisovskiy
+ Vladislav Grishenko
+ Lubomir Rintel
+ Frank Schmirler
+
+Device information contributors are named in the "device_reference.txt" file.
+
+JimTcl is currently maintained by Steve Bennett; see README in subfolder
+ for details. It is released under a FreeBSD-style license.
+ Visit http://jim.tcl.tk/
+
+
+
+Legal
+=====
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU General Public License for more details:
+
+http://www.gnu.org/licenses/gpl.txt
+
+Or find it as the file COPYING in this folder.
+
+
+
+
+Last revised: 2017-12-31, Josua Dietze
diff --git a/dispatcher.c b/dispatcher.c
new file mode 100644
index 0000000..f30132f
--- /dev/null
+++ b/dispatcher.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011-2017 Josua Dietze, usb_modeswitch version 2.5.2
+ * Contains code under
+ * Copyright (c) 2010 Wojciech A. Koszek <wkoszek@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <jim.h>
+
+/* RAW is defined to the complete Tcl code in that file: */
+#include "usb_modeswitch.string"
+
+#define MAX_ARGSIZE 1024
+
+int main(int argc, char **argv)
+{
+ int i, retval;
+ char arg[MAX_ARGSIZE];
+ char arglist[MAX_ARGSIZE];
+
+ Jim_Interp *interp;
+
+ interp = NULL;
+ arglist[0] = '\0';
+
+ for (i=1; i<argc; i++) {
+ if (strlen(argv[i]) > MAX_ARGSIZE-4) {
+ printf("Argument #%d extends maximum size, skip it\n", i);
+ continue;
+ }
+ if ( (strlen(arglist) + strlen(argv[i])) > MAX_ARGSIZE-4) {
+ printf("Argument #%d would extend maximum list size, skip it\n", i);
+ continue;
+ }
+ sprintf(arg,"{%s} ",argv[i]);
+ strncat(arglist,arg,MAX_ARGSIZE-1);
+ }
+
+ char code[sizeof(RAW) + sizeof(arglist) + 28];
+ sprintf(code, "set argv {%s}\nset argc %d\n", arglist, argc-1);
+ strncat(code, RAW, sizeof(RAW));
+
+ /* Create Jim instance */
+ interp = Jim_CreateInterp();
+ assert(interp != NULL && "Could not create interpreter!");
+
+ /* Register base commands, actually implementing Tcl */
+ Jim_RegisterCoreCommands(interp);
+
+ /* Initialize any static extensions */
+ Jim_InitStaticExtensions(interp);
+
+ /* Evaluate the script that's now in "code" */
+ retval = Jim_Eval(interp, code);
+ if (retval < 0)
+ printf("Evaluation returned error %d\n", retval);
+
+ /* Free the interpreter */
+ Jim_FreeInterp(interp);
+ return (EXIT_SUCCESS);
+}
diff --git a/make_string.tcl b/make_string.tcl
new file mode 100755
index 0000000..5a1b5ff
--- /dev/null
+++ b/make_string.tcl
@@ -0,0 +1,31 @@
+#!/usr/bin/env tclsh
+
+# (c) Josua Dietze 2012
+#
+# Usage: make_string.tcl source.tcl >jim-source.c
+
+# Converts a Tcl source file into C source suitable
+# for using as an embedded script.
+
+set source [lindex $argv 0]
+
+if {![string match *.tcl $source]} {
+ error "Source $source is not a .tcl file"
+}
+
+# Read the Tcl source and convert to C macro
+set sourcelines {}
+set f [open $source]
+while {[gets $f buf] >= 0} {
+ # Remove comment lines
+ regsub {^[ \t]*#.*$} $buf "" buf
+ # Remove leading whitespaces
+ set buf [string trimleft $buf]
+ # Escape quotes and backlashes
+ set buf [string map [list \\ \\\\ \" \\"] $buf]
+ if [string length $buf] {
+ lappend sourcelines "$buf\\n"
+ }
+}
+close $f
+puts "#define RAW \"[join $sourcelines ""]\""
diff --git a/usb-modeswitch-upstart.conf b/usb-modeswitch-upstart.conf
new file mode 100644
index 0000000..0d82b69
--- /dev/null
+++ b/usb-modeswitch-upstart.conf
@@ -0,0 +1,5 @@
+start on usb-modeswitch-upstart
+task
+script
+ exec /usr/sbin/usb_modeswitch_dispatcher --switch-mode $UMS_PARAM
+end script
diff --git a/usb_modeswitch.1 b/usb_modeswitch.1
new file mode 100644
index 0000000..d91135f
--- /dev/null
+++ b/usb_modeswitch.1
@@ -0,0 +1,175 @@
+.TH "USB_MODESWITCH" "1"
+.SH "NAME"
+usb_modeswitch - control the mode of 'multi-state' USB devices
+.SH "SYNOPSIS"
+.PP
+\fBusb_modeswitch\fR [\fB\-heWQDIvpVPmM23rwKdHSOBGTNALnsRiuagft\fP] [\fB\-c \fIfilename\fP]
+.SH "DESCRIPTION"
+.PP
+Several new USB devices have their proprietary Windows drivers onboard,
+most of them WWAN and WLAN dongles. When plugged in for the first time,
+they act like a flash storage and start installing the Windows driver from
+there. If the driver is installed, it makes the storage device disappear
+and a new device, mainly composite (e.g. with modem ports), shows up.
+.PP
+On Linux, in most cases the drivers are available as kernel modules,
+such as "usbserial" or "option". However, the device initially binds to
+"usb-storage" by default. \fBusb_modeswitch\fR can then send a provided bulk
+message (most likely a mass storage command) to the device; this message
+has to be determined by analyzing the actions of the Windows driver.
+.PP
+In some cases, USB control commands are used for switching. These cases are
+handled by custom functions, and no bulk message needs to be provided.
+.PP
+Usually, the program is distributed with a set of configurations for many
+known devices, which allows a fully automatic handling of a device upon
+insertion, made possible by combining usb_modeswitch with the wrapper script
+\fBusb_modeswitch_dispatcher\fR which is launched by the udev daemon. This
+requires a Linux-flavoured system though.
+.PP
+Note that \fBusb_modeswitch\fR itself has no specific Linux dependencies.
+
+.SH "OPTIONS"
+.PP
+This program follows the usual GNU command line syntax,
+with long options starting with two dashes ('--'). A summary of
+options is included below.
+.IP "\fB-h\fP \fB\-\-help\fP " 10
+Show summary of options.
+.IP "\fB-e\fP \fB\-\-version\fP " 10
+Print version information and exit
+.IP "\fB-v\fP \fB\-\-default-vendor NUM\fP " 10
+Vendor ID to look for (mandatory), usually given as hex number (example: 0x12d1).
+Each USB device is identified by a number
+officialy assigned to the vendor by the USB association and a number for the
+respective model (product ID) chosen by the vendor
+.IP "\fB-p\fP \fB\-\-default-product NUM\fP " 10
+Product ID to look for (mandatory)
+.IP "\fB-V\fP \fB\-\-target-vendor NUM\fP " 10
+Target vendor ID. When given will be searched for and detected initially
+for information purposes. If success checking (option \-s) is active,
+providing target IDs (vendor/product) or target class is recommended
+.IP "\fB-j\fP \fB\-\-find-mbim\fP " 10
+Return configuration number with MBIM interface and exit.
+.IP "\fB-P\fP \fB\-\-target-product NUM\fP " 10
+Target product ID
+.IP "\fB-b\fP \fB\-\-bus-num NUM\fP " 10
+.IP "\fB-g\fP \fB\-\-device-num NUM\fP " 10
+If bus and device number are provided, the handling of a specific device on
+a specific USB port is guaranteed, in contrast to using only the USB ID. This
+is important if there are multiple similar devices on a system
+.IP "\fB-C\fP \fB\-\-target-class NUM\fP " 10
+Target Device Class according to the USB specification. Some devices keep
+their original vendor/product ID after successful switching. To prevent
+them from being treated again, the device class can be checked.
+For unswitched devices it is always 8 (storage class), for switched
+modems it is often 0xff (vendor specific). In composite modes,
+the class of the first interface is watched
+.IP "\fB-m\fP \fB\-\-message-endpoint NUM\fP " 10
+A specific endpoint to use for data transfers. Only for testing purposes; usually
+endpoints are determined from the device attributes
+.IP "\fB-M\fP \fB\-\-message-content STRING\fP " 10
+A bulk message to send as a switching command. Provided as a hexadecimal string
+.IP "\fB-2, -3\fP \fB\-\-message-content2, \-\-message-content3 STRING\fP " 10
+Additional bulk messages to send as switching commands. Provided as hexadecimal strings.
+When used with mass storage commands, setting \fB\-\-need-response\fR is
+strongly advised to comply with specifications and to avoid likely errors
+.IP "\fB-w\fP \fB\-\-release-delay NUM\fP " 10
+After issuing all bulk messages, wait for NUM milliseconds before releasing the interface.
+Required for some modems on older systems (especially after an EJECT message)
+.IP "\fB-n\fP \fB\-\-need-response\fP " 10
+Obsolete. CSW is always attempted to being read after mass storage transfers. No downside
+.IP "\fB-r\fP \fB\-\-response-endpoint NUM\fP " 10
+Try to read the response to a storage command from there. Only for testing purposes;
+usually endpoints are determined from the device attributes
+.IP "\fB-K\fP \fB\-\-std-eject\fP " 10
+Apply the standard SCSI sequence of "Allow Medium Removal" and
+"Eject". Implies \fB-n\fP. One 'Message' can be added with \fB-M\fP
+that will be transmitted after the eject sequence. Used by many modems
+.IP "\fB-d\fP \fB\-\-detach-only\fP " 10
+Just detach the current driver. This is sufficient for some early
+devices to switch successfully. Otherwise this feature can
+be used as a 'scalpel' for special cases, like separating the
+driver from individual interfaces
+.IP "\fB-H\fP \fB\-\-huawei-mode\fP " 10
+Send a special control message used by older Huawei devices
+.IP "\fB-J\fP \fB\-\-huawei-new-mode\fP " 10
+Send a specific bulk message used by all newer Huawei devices
+.IP "\fB-X\fP \fB\-\-huawei-alt-mode\fP " 10
+Send an alternative bulk message to Huawei devices
+.IP "\fB-S\fP \fB\-\-sierra-mode\fP " 10
+Send a special control message used by Sierra devices
+.IP "\fB-G\fP \fB\-\-gct-mode\fP " 10
+Send a special control message used by GCT chipsets
+.IP "\fB-T\fP \fB\-\-kobil-mode\fP " 10
+Send a special control message used by Kobil devices
+.IP "\fB-N\fP \fB\-\-sequans-mode\fP " 10
+Send a special control message used by Sequans chipset
+.IP "\fB-A\fP \fB\-\-mobileaction-mode\fP " 10
+Send a special control message used by the MobileAction device
+.IP "\fB-B\fP \fB\-\-qisda-mode\fP " 10
+Send a special control message used by Qisda devices
+.IP "\fB-E\fP \fB\-\-quanta-mode\fP " 10
+Send a special control message used by Quanta devices
+.IP "\fB-F\fP \fB\-\-pantech-mode NUM\fP " 10
+Send a special control message used by Pantech devices.
+Value NUM will be used in control message as 'wValue'
+.IP "\fB-Z\fP \fB\-\-blackberry-mode\fP " 10
+Send a special control message used by some newer Blackberry devices
+.IP "\fB-S\fP \fB\-\-option-mode\fP " 10
+Send a special control message used by all Option devices
+.IP "\fB-O\fP \fB\-\-sony-mode\fP " 10
+Apply a special sequence used by Sony Ericsson devices. Implies option \--check-success
+.IP "\fB-L\fP \fB\-\-cisco-mode\fP " 10
+Send a sequence of bulk messages used by Cisco devices
+.IP "\fB-R\fP \fB\-\-reset-usb\fP " 10
+Send a USB reset command to the device. Can be combined with any switching
+method or stand alone. It is always done as the last step of all device
+interactions.
+Few devices need it to complete the switching; apart from that it may be
+useful during testing
+.IP "\fB-c\fP \fB\-\-config-file FILENAME\fP " 10
+Use a specific config file. If any ID or switching options are given as
+command line parameters, this option is ignored.
+In that case all mandatory parameters have to be provided on
+the command line
+.IP "\fB-f\fP \fB\-\-long-config STRING\fP " 10
+Provide device details in config file syntax as a multiline string
+on the command line
+.IP "\fB-t\fP \fB\-\-stdinput\fP " 10
+Read the device details in config file syntax from standard input, e.g. redirected from
+a command pipe (multiline text)
+.IP "\fB-Q\fP \fB\-\-quiet\fP " 10
+Don't show progress or error messages
+.IP "\fB-W\fP \fB\-\-verbose\fP " 10
+Print all settings before running and show libusb debug messages
+.IP "\fB-D\fP \fB\-\-sysmode\fP " 10
+Changes the behaviour of the program slightly. A success message including the
+effective target device ID is put out and a syslog notice is issued. Mainly for
+integration with a wrapper script
+.IP "\fB-s\fP \fB\-\-check-success NUM\fP " 10
+After switching, keep checking for the result up to max. NUM seconds. If target IDs
+or target class were provided, their appearance indicates certain success. Otherwise
+the disconnection of the original device is rated as likely proof
+.IP "\fB-I\fP \fB\-\-inquire\fP " 10
+Obsolete. Formerly obtained SCSI attributes, now ignored
+.IP "\fB-i\fP \fB\-\-interface NUM\fP " 10
+Select initial USB interface (default: 0). Only for testing purposes
+.IP "\fB-u\fP \fB\-\-configuration NUM\fP " 10
+Select USB configuration (applied after any other possible switching actions)
+.IP "\fB-a\fP \fB\-\-altsetting NUM\fP " 10
+Select alternative USB interface setting (applied after switching). Mainly
+for testing
+.SH "AUTHOR"
+.PP
+This manual page was originally written by Didier Raboud (didier@raboud.com) for
+the \fBDebian\fP system. Additions made by Josua Dietze. Permission is
+granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 or any
+later version published by the Free Software Foundation.
+
+.PP
+The complete text of the current GNU General Public
+License can be found in http://www.gnu.org/licenses/gpl.txt
+
+.\" last edited 2017-08-06 for version 2.5.1
diff --git a/usb_modeswitch.c b/usb_modeswitch.c
new file mode 100644
index 0000000..3239946
--- /dev/null
+++ b/usb_modeswitch.c
@@ -0,0 +1,2115 @@
+/*
+ Mode switching tool for controlling mode of 'multi-state' USB devices
+ Version 2.5.2, 2017/12/31
+
+ Copyright (C) 2007 - 2017 Josua Dietze (mail to "usb_admin" at the domain
+ of the home page; or write a personal message through the forum to "Josh".
+ NO SUPPORT VIA E-MAIL - please use the forum for that)
+
+ Major contributions:
+
+ Command line parsing, decent usage/config output/handling, bugfixes and advanced
+ options added by:
+ Joakim Wennergren
+
+ TargetClass parameter implementation to support new Option devices/firmware:
+ Paul Hardwick (http://www.pharscape.org)
+
+ Created with initial help from:
+ "usbsnoop2libusb.pl" by Timo Lindfors (http://iki.fi/lindi/usb/usbsnoop2libusb.pl)
+
+ Config file parsing code borrowed from:
+ Guillaume Dargaud (http://www.gdargaud.net/Hack/SourceCode.html)
+
+ Hexstr2bin function borrowed from:
+ Jouni Malinen (http://hostap.epitest.fi/wpa_supplicant, from "common.c")
+
+ Other contributions: see README
+
+ Device information contributors are named in the "device_reference.txt" file. See
+ homepage.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details:
+
+ http://www.gnu.org/licenses/gpl.txt
+
+*/
+
+/* Recommended tab size: 4 */
+
+#define VERSION "2.5.2"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "usb_modeswitch.h"
+
+
+// Little helpers
+
+int usb_bulk_io(struct libusb_device_handle *handle, int ep, unsigned char *bytes,
+ int size, int timeout)
+{
+ int actual_length;
+ int r;
+// usbi_dbg("endpoint %x size %d timeout %d", ep, size, timeout);
+ r = libusb_bulk_transfer(handle, ep & 0xff, bytes, size,
+ &actual_length, timeout);
+
+ /* if we timed out but did transfer some data, report as successful short
+ * read. FIXME: is this how libusb-0.1 works? */
+ if (r == 0 || (r == LIBUSB_ERROR_TIMEOUT && actual_length > 0))
+ return actual_length;
+
+ return r;
+}
+
+
+static int usb_interrupt_io(libusb_device_handle *handle, int ep, unsigned char *bytes,
+ int size, int timeout)
+{
+ int actual_length;
+ int r;
+// usbi_dbg("endpoint %x size %d timeout %d", ep, size, timeout);
+ r = libusb_interrupt_transfer(handle, ep & 0xff, bytes, size,
+ &actual_length, timeout);
+
+ /* if we timed out but did transfer some data, report as successful short
+ * read. FIXME: is this how libusb-0.1 works? */
+ if (r == 0 || (r == LIBUSB_ERROR_TIMEOUT && actual_length > 0))
+ return actual_length;
+
+ return (r);
+}
+
+
+#define LINE_DIM 1024
+#define MSG_DIM 11
+#define MAXLINES 50
+#define BUF_SIZE 4096
+#define DESCR_MAX 129
+
+#define SEARCH_DEFAULT 0
+#define SEARCH_TARGET 1
+#define SEARCH_BUSDEV 2
+
+#define SWITCH_CONFIG_MAXTRIES 5
+
+#define SHOW_PROGRESS if (show_progress) fprintf
+
+char *TempPP=NULL;
+
+static struct libusb_context *ctx = NULL;
+static struct libusb_device *dev = NULL;
+static struct libusb_device_handle *devh = NULL;
+static struct libusb_config_descriptor *active_config = NULL;
+
+int DefaultVendor=0, DefaultProduct=0, TargetVendor=0, TargetProduct=-1, TargetClass=0;
+int MessageEndpoint=0, ResponseEndpoint=0, ReleaseDelay=0;
+int targetDeviceCount=0, searchMode;
+int devnum=-1, busnum=-1;
+
+unsigned int ModeMap = 0;
+#define DETACHONLY_MODE 0x00000001
+#define HUAWEI_MODE 0x00000002
+#define SIERRA_MODE 0x00000004
+#define SONY_MODE 0x00000008
+#define GCT_MODE 0x00000010
+#define KOBIL_MODE 0x00000020
+#define SEQUANS_MODE 0x00000040
+#define MOBILEACTION_MODE 0x00000080
+#define CISCO_MODE 0x00000100
+#define QISDA_MODE 0x00000200
+#define QUANTA_MODE 0x00000400
+#define BLACKBERRY_MODE 0x00000800
+#define PANTECH_MODE 0x00001000
+#define HUAWEINEW_MODE 0x00002000
+#define OPTION_MODE 0x00004000
+#define HUAWEIALT_MODE 0x00008000
+
+
+int PantechMode=0;
+char verbose=0, show_progress=1, ResetUSB=0, CheckSuccess=0, config_read=0;
+char NoDriverLoading=0, sysmode=0, mbim=0;
+char StandardEject=0;
+
+char MessageContent[LINE_DIM];
+char MessageContent2[LINE_DIM];
+char MessageContent3[LINE_DIM];
+char TargetProductList[LINE_DIM];
+char DefaultProductList[5];
+unsigned char ByteString[LINE_DIM/2];
+unsigned char buffer[BUF_SIZE];
+char **Messages = NULL;
+
+FILE *output;
+
+
+/* Settable interface, altsetting (for debugging mostly) and configuration */
+int Interface = -1, Configuration = 0, AltSetting = -1;
+
+
+static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'e'},
+ {"default-vendor", required_argument, 0, 'v'},
+ {"default-product", required_argument, 0, 'p'},
+ {"target-vendor", required_argument, 0, 'V'},
+ {"target-product", required_argument, 0, 'P'},
+ {"target-class", required_argument, 0, 'C'},
+ {"message-endpoint", required_argument, 0, 'm'},
+ {"message-content", required_argument, 0, 'M'},
+ {"message-content2", required_argument, 0, '2'},
+ {"message-content3", required_argument, 0, '3'},
+ {"release-delay", required_argument, 0, 'w'},
+ {"response-endpoint", required_argument, 0, 'r'},
+ {"bus-num", required_argument, 0, 'b'},
+ {"device-num", required_argument, 0, 'g'},
+ {"detach-only", no_argument, 0, 'd'},
+ {"huawei-mode", no_argument, 0, 'H'},
+ {"huawei-new-mode", no_argument, 0, 'J'},
+ {"huawei-alt-mode", no_argument, 0, 'X'},
+ {"sierra-mode", no_argument, 0, 'S'},
+ {"sony-mode", no_argument, 0, 'O'},
+ {"qisda-mode", no_argument, 0, 'B'},
+ {"quanta-mode", no_argument, 0, 'E'},
+ {"kobil-mode", no_argument, 0, 'T'},
+ {"gct-mode", no_argument, 0, 'G'},
+ {"sequans-mode", no_argument, 0, 'N'},
+ {"mobileaction-mode", no_argument, 0, 'A'},
+ {"cisco-mode", no_argument, 0, 'L'},
+ {"blackberry-mode", no_argument, 0, 'Z'},
+ {"option-mode", no_argument, 0, 'U'},
+ {"pantech-mode", required_argument, 0, 'F'},
+ {"std-eject", no_argument, 0, 'K'},
+ {"need-response", no_argument, 0, 'n'},
+ {"reset-usb", no_argument, 0, 'R'},
+ {"config-file", required_argument, 0, 'c'},
+ {"verbose", no_argument, 0, 'W'},
+ {"quiet", no_argument, 0, 'Q'},
+ {"sysmode", no_argument, 0, 'D'},
+ {"inquire", no_argument, 0, 'I'},
+ {"stdinput", no_argument, 0, 't'},
+ {"find-mbim", no_argument, 0, 'j'},
+ {"long-config", required_argument, 0, 'f'},
+ {"check-success", required_argument, 0, 's'},
+ {"interface", required_argument, 0, 'i'},
+ {"configuration", required_argument, 0, 'u'},
+ {"altsetting", required_argument, 0, 'a'},
+ {0, 0, 0, 0}
+};
+
+
+void readConfigFile(const char *configFilename)
+{
+ ParseParamHex(configFilename, TargetVendor);
+ ParseParamHex(configFilename, TargetProduct);
+ ParseParamString(configFilename, TargetProductList);
+ ParseParamHex(configFilename, TargetClass);
+ ParseParamHex(configFilename, DefaultVendor);
+ ParseParamHex(configFilename, DefaultProduct);
+ ParseParamBoolMap(configFilename, DetachStorageOnly, ModeMap, DETACHONLY_MODE);
+ ParseParamBoolMap(configFilename, HuaweiMode, ModeMap, HUAWEI_MODE);
+ ParseParamBoolMap(configFilename, HuaweiNewMode, ModeMap, HUAWEINEW_MODE);
+ ParseParamBoolMap(configFilename, HuaweiAltMode, ModeMap, HUAWEIALT_MODE);
+ ParseParamBoolMap(configFilename, SierraMode, ModeMap, SIERRA_MODE);
+ ParseParamBoolMap(configFilename, SonyMode, ModeMap, SONY_MODE);
+ ParseParamBoolMap(configFilename, GCTMode, ModeMap, GCT_MODE);
+ ParseParamBoolMap(configFilename, KobilMode, ModeMap, KOBIL_MODE);
+ ParseParamBoolMap(configFilename, SequansMode, ModeMap, SEQUANS_MODE);
+ ParseParamBoolMap(configFilename, MobileActionMode, ModeMap, MOBILEACTION_MODE);
+ ParseParamBoolMap(configFilename, CiscoMode, ModeMap, CISCO_MODE);
+ ParseParamBoolMap(configFilename, QisdaMode, ModeMap, QISDA_MODE);
+ ParseParamBoolMap(configFilename, QuantaMode, ModeMap, QUANTA_MODE);
+ ParseParamBoolMap(configFilename, OptionMode, ModeMap, OPTION_MODE);
+ ParseParamBoolMap(configFilename, BlackberryMode, ModeMap, BLACKBERRY_MODE);
+ ParseParamInt(configFilename, PantechMode);
+ if (PantechMode)
+ ModeMap |= PANTECH_MODE;
+ ParseParamBool(configFilename, StandardEject);
+ ParseParamBool(configFilename, NoDriverLoading);
+ ParseParamHex(configFilename, MessageEndpoint);
+ ParseParamString(configFilename, MessageContent);
+ ParseParamString(configFilename, MessageContent2);
+ ParseParamString(configFilename, MessageContent3);
+ ParseParamInt(configFilename, ReleaseDelay);
+ ParseParamHex(configFilename, ResponseEndpoint);
+ ParseParamHex(configFilename, ResetUSB);
+ ParseParamInt(configFilename, CheckSuccess);
+ ParseParamHex(configFilename, Interface);
+ ParseParamHex(configFilename, Configuration);
+ ParseParamHex(configFilename, AltSetting);
+
+ /* TargetProductList has priority over TargetProduct */
+ if (TargetProduct != -1 && TargetProductList[0] != '\0') {
+ TargetProduct = -1;
+ SHOW_PROGRESS(output,"Warning: TargetProductList overrides TargetProduct!\n");
+ }
+
+ config_read = 1;
+}
+
+
+void printConfig()
+{
+ if ( DefaultVendor )
+ fprintf (output,"DefaultVendor= 0x%04x\n", DefaultVendor);
+ if ( DefaultProduct )
+ fprintf (output,"DefaultProduct= 0x%04x\n", DefaultProduct);
+ if ( TargetVendor )
+ fprintf (output,"TargetVendor= 0x%04x\n", TargetVendor);
+ if ( TargetProduct > -1 )
+ fprintf (output,"TargetProduct= 0x%04x\n", TargetProduct);
+ if ( TargetClass )
+ fprintf (output,"TargetClass= 0x%02x\n", TargetClass);
+ if ( strlen(TargetProductList) )
+ fprintf (output,"TargetProductList=\"%s\"\n", TargetProductList);
+ if (StandardEject)
+ fprintf (output,"\nStandardEject=1\n");
+ if (ModeMap & DETACHONLY_MODE)
+ fprintf (output,"\nDetachStorageOnly=1\n");
+ if (ModeMap & HUAWEI_MODE)
+ fprintf (output,"HuaweiMode=1\n");
+ if (ModeMap & HUAWEINEW_MODE)
+ fprintf (output,"HuaweiNewMode=1\n");
+ if (ModeMap & HUAWEIALT_MODE)
+ fprintf (output,"HuaweiAltMode=1\n");
+ if (ModeMap & SIERRA_MODE)
+ fprintf (output,"SierraMode=1\n");
+ if (ModeMap & SONY_MODE)
+ fprintf (output,"SonyMode=1\n");
+ if (ModeMap & QISDA_MODE)
+ fprintf (output,"QisdaMode=1\n");
+ if (ModeMap & QUANTA_MODE)
+ fprintf (output,"QuantaMode=1\n");
+ if (ModeMap & GCT_MODE)
+ fprintf (output,"GCTMode=1\n");
+ if (ModeMap & KOBIL_MODE)
+ fprintf (output,"KobilMode=1\n");
+ if (ModeMap & SEQUANS_MODE)
+ fprintf (output,"SequansMode=1\n");
+ if (ModeMap & MOBILEACTION_MODE)
+ fprintf (output,"MobileActionMode=1\n");
+ if (ModeMap & CISCO_MODE)
+ fprintf (output,"CiscoMode=1\n");
+ if (ModeMap & BLACKBERRY_MODE)
+ fprintf (output,"BlackberryMode=1\n");
+ if (ModeMap & OPTION_MODE)
+ fprintf (output,"OptionMode=1\n");
+ if (ModeMap & PANTECH_MODE)
+ fprintf (output,"PantechMode=1\n");
+ if ( MessageEndpoint )
+ fprintf (output,"MessageEndpoint=0x%02x\n", MessageEndpoint);
+ if ( strlen(MessageContent) )
+ fprintf (output,"MessageContent=\"%s\"\n", MessageContent);
+ if ( strlen(MessageContent2) )
+ fprintf (output,"MessageContent2=\"%s\"\n", MessageContent2);
+ if ( strlen(MessageContent3) )
+ fprintf (output,"MessageContent3=\"%s\"\n", MessageContent3);
+ if ( ResponseEndpoint )
+ fprintf (output,"ResponseEndpoint=0x%02x\n", ResponseEndpoint);
+ if ( Interface > -1 )
+ fprintf (output,"Interface=0x%02x\n", Interface);
+ if ( Configuration > 0 )
+ fprintf (output,"Configuration=0x%02x\n", Configuration);
+ if ( AltSetting > -1 )
+ fprintf (output,"AltSetting=0x%02x\n", AltSetting);
+ if ( CheckSuccess )
+ fprintf (output,"Success check enabled, max. wait time %d seconds\n", CheckSuccess);
+ if ( sysmode )
+ fprintf (output,"System integration mode enabled\n");
+}
+
+
+int readArguments(int argc, char **argv)
+{
+ int c, option_index = 0, count=0;
+ char *longConfig = NULL;
+ if (argc==1)
+ {
+ printHelp();
+ printVersion();
+ exit(1);
+ }
+
+ while (1)
+ {
+ c = getopt_long (argc, argv, "hejWQDndKHJSOBEGTNALZUXF:RItv:p:V:P:C:m:M:2:3:w:r:c:i:u:a:s:f:b:g:",
+ long_options, &option_index);
+
+ /* Detect the end of the options. */
+ if (c == -1)
+ break;
+ count++;
+ switch (c)
+ {
+ case 'R': ResetUSB = 1; break;
+ case 'v': DefaultVendor = strtol(optarg, NULL, 16); break;
+ case 'p': DefaultProduct = strtol(optarg, NULL, 16); break;
+ case 'V': TargetVendor = strtol(optarg, NULL, 16); break;
+ case 'P': TargetProduct = strtol(optarg, NULL, 16); break;
+ case 'C': TargetClass = strtol(optarg, NULL, 16); break;
+ case 'm': MessageEndpoint = strtol(optarg, NULL, 16); break;
+ case 'M': strncpy(MessageContent, optarg, LINE_DIM); break;
+ case '2': strncpy(MessageContent2, optarg, LINE_DIM); break;
+ case '3': strncpy(MessageContent3, optarg, LINE_DIM); break;
+ case 'w': ReleaseDelay = strtol(optarg, NULL, 10); break;
+ case 'n': break;
+ case 'r': ResponseEndpoint = strtol(optarg, NULL, 16); break;
+ case 'K': StandardEject = 1; break;
+ case 'd': ModeMap = ModeMap + DETACHONLY_MODE; break;
+ case 'H': ModeMap = ModeMap + HUAWEI_MODE; break;
+ case 'J': ModeMap = ModeMap + HUAWEINEW_MODE; break;
+ case 'X': ModeMap = ModeMap + HUAWEIALT_MODE; break;
+ case 'S': ModeMap = ModeMap + SIERRA_MODE; break;
+ case 'O': ModeMap = ModeMap + SONY_MODE; break;; break;
+ case 'B': ModeMap = ModeMap + QISDA_MODE; break;
+ case 'E': ModeMap = ModeMap + QUANTA_MODE; break;
+ case 'G': ModeMap = ModeMap + GCT_MODE; break;
+ case 'T': ModeMap = ModeMap + KOBIL_MODE; break;
+ case 'N': ModeMap = ModeMap + SEQUANS_MODE; break;
+ case 'A': ModeMap = ModeMap + MOBILEACTION_MODE; break;
+ case 'L': ModeMap = ModeMap + CISCO_MODE; break;
+ case 'Z': ModeMap = ModeMap + BLACKBERRY_MODE; break;
+ case 'U': ModeMap = ModeMap + OPTION_MODE; break;
+ case 'F': ModeMap = ModeMap + PANTECH_MODE;
+ PantechMode = strtol(optarg, NULL, 10); break;
+ case 'c': readConfigFile(optarg); break;
+ case 't': readConfigFile("stdin"); break;
+ case 'W': verbose = 1; show_progress = 1; count--; break;
+ case 'Q': show_progress = 0; verbose = 0; count--; break;
+ case 'D': sysmode = 1; count--; break;
+ case 's': CheckSuccess = strtol(optarg, NULL, 10); count--; break;
+ case 'I': break;
+ case 'b': busnum = strtol(optarg, NULL, 10); break;
+ case 'g': devnum = strtol(optarg, NULL, 10); break;
+
+ case 'i': Interface = strtol(optarg, NULL, 16); break;
+ case 'u': Configuration = strtol(optarg, NULL, 16); break;
+ case 'a': AltSetting = strtol(optarg, NULL, 16); break;
+ case 'j': mbim = 1; break;
+
+ case 'f':
+ longConfig = malloc(strlen(optarg)+5);
+ strcpy(longConfig,"##\n");
+ strcat(longConfig,optarg);
+ strcat(longConfig,"\n");
+ readConfigFile(longConfig);
+ free(longConfig);
+ break;
+
+ case 'e':
+ printVersion();
+ exit(0);
+ break;
+ case 'h':
+ printVersion();
+ printHelp();
+ exit(0);
+ break;
+
+ default: /* Unsupported - error message has already been printed */
+ fprintf (output,"\n");
+ printHelp();
+ exit(1);
+ }
+ }
+ return count;
+}
+
+
+int main(int argc, char **argv)
+{
+ int ret=0, numDefaults=0, sonySuccess=0, i;
+ int defaultClass=0, interfaceClass=0, currentConfigVal=0;
+ struct libusb_device_descriptor descriptor;
+ enum libusb_error libusbError;
+
+ /* Make sure we have empty strings even if not set by config */
+ TargetProductList[0] = '\0';
+ MessageContent[0] = '\0';
+ MessageContent2[0] = '\0';
+ MessageContent3[0] = '\0';
+ DefaultProductList[0] = '\0';
+
+ /* Useful for debugging during boot */
+// output=fopen("/dev/console", "w");
+ output=stdout;
+
+ signal(SIGTERM, release_usb_device);
+
+ /*
+ * Parameter parsing, USB preparation/diagnosis, plausibility checks
+ */
+
+ /* Check command arguments, use params instead of config file when given */
+ switch (readArguments(argc, argv)) {
+ case 0: /* no argument or -W, -q or -s */
+ break;
+ default: /* one or more arguments except -W, -q or -s */
+ if (!config_read) /* if arguments contain -c, the config file was already processed */
+ if (verbose) fprintf(output,"Take all parameters from the command line\n\n");
+ }
+
+ if (verbose) {
+ printVersion();
+ printConfig();
+ fprintf(output,"\n");
+ }
+
+ /* Some sanity checks. The default IDs are mandatory */
+ if (!(DefaultVendor && DefaultProduct)) {
+ SHOW_PROGRESS(output,"No default vendor/product ID given. Abort\n\n");
+ exit(1);
+ }
+
+ if (strlen(MessageContent)) {
+ if (strlen(MessageContent) % 2 != 0) {
+ fprintf(stderr, "MessageContent hex string has uneven length. Abort\n\n");
+ exit(1);
+ }
+ if ( hexstr2bin(MessageContent, ByteString, strlen(MessageContent)/2) == -1) {
+ fprintf(stderr, "MessageContent %s\n is not a hex string. Abort\n\n",
+ MessageContent);
+
+ exit(1);
+ }
+ }
+
+ if (devnum == -1) {
+ searchMode = SEARCH_DEFAULT;
+ } else {
+ SHOW_PROGRESS(output,"Use given bus/device number: %03d/%03d ...\n", busnum, devnum);
+ searchMode = SEARCH_BUSDEV;
+ }
+
+ if (show_progress)
+ if (CheckSuccess && !(TargetVendor || TargetProduct > -1 || TargetProductList[0] != '\0')
+ && !TargetClass)
+
+ fprintf(output,"Note: No target parameter given; success check limited\n");
+
+ if (TargetProduct > -1 && TargetProductList[0] == '\0') {
+ sprintf(TargetProductList,"%04x",TargetProduct);
+ TargetProduct = -1;
+ }
+
+ /* libusb initialization */
+ if ((libusbError = libusb_init(&ctx)) != LIBUSB_SUCCESS) {
+ fprintf(stderr, "Error: Failed to initialize libusb. %s (%d)\n\n",
+ libusb_error_name(libusbError), libusbError);
+ exit(1);
+ }
+
+ if (verbose)
+ libusb_set_debug(ctx, 3);
+
+ if (mbim) {
+ printf("%d\n", findMBIMConfig(DefaultVendor, DefaultProduct, searchMode) );
+ exit(0);
+ }
+
+ /* Count existing target devices, remember for success check */
+ if (searchMode != SEARCH_BUSDEV && (TargetVendor || TargetClass)) {
+ SHOW_PROGRESS(output,"Look for target devices ...\n");
+ search_devices(&targetDeviceCount, TargetVendor, TargetProductList, TargetClass, 0,
+ SEARCH_TARGET);
+
+ if (targetDeviceCount) {
+ SHOW_PROGRESS(output," Found devices in target mode or class (%d)\n", targetDeviceCount);
+ } else
+ SHOW_PROGRESS(output," No devices in target mode or class found\n");
+ }
+
+ /* Count default devices, get the last one found */
+ SHOW_PROGRESS(output,"Look for default devices ...\n");
+
+ sprintf(DefaultProductList,"%04x",DefaultProduct);
+ dev = search_devices(&numDefaults, DefaultVendor, DefaultProductList, TargetClass,
+ Configuration, searchMode);
+
+ if (numDefaults) {
+ SHOW_PROGRESS(output," Found devices in default mode (%d)\n", numDefaults);
+ } else {
+ SHOW_PROGRESS(output," No devices in default mode found. Nothing to do. Bye!\n\n");
+ close_all();
+ exit(0);
+ }
+
+ if (dev == NULL) {
+ SHOW_PROGRESS(output," No bus/device match. Is device connected? Abort\n\n");
+ close_all();
+ exit(0);
+ } else {
+ if (devnum == -1) {
+ devnum = libusb_get_device_address(dev);
+ busnum = libusb_get_bus_number(dev);
+ SHOW_PROGRESS(output,"Access device %03d on bus %03d\n", devnum, busnum);
+ }
+ libusb_open(dev, &devh);
+ if (devh == NULL) {
+ SHOW_PROGRESS(output,"Error opening the device. Abort\n\n");
+ abortExit();
+ }
+ }
+
+ /* Get current configuration of default device, note value if Configuration
+ * parameter is set. Also sets active_config
+ */
+ currentConfigVal = get_current_config_value(dev);
+ if (Configuration > -1) {
+ SHOW_PROGRESS(output,"Current configuration number is %d\n", currentConfigVal);
+ } else
+ currentConfigVal = 0;
+
+ libusb_get_device_descriptor(dev, &descriptor);
+ defaultClass = descriptor.bDeviceClass;
+ if (Interface == -1)
+ Interface = active_config->interface[0].altsetting[0].bInterfaceNumber;
+ SHOW_PROGRESS(output,"Use interface number %d\n", Interface);
+
+ /* Get class of default device/interface */
+ interfaceClass = get_interface_class();
+
+ if (interfaceClass == -1) {
+ fprintf(stderr, "Error: Could not get class of interface %d. Does it exist? Abort\n\n",Interface);
+ abortExit();
+ } else {
+ SHOW_PROGRESS(output," with class %d\n", interfaceClass);
+ }
+
+ if (defaultClass == 0 || defaultClass == 0xef)
+ defaultClass = interfaceClass;
+ else
+ if (interfaceClass == LIBUSB_CLASS_MASS_STORAGE && defaultClass != LIBUSB_CLASS_MASS_STORAGE
+ && defaultClass != LIBUSB_CLASS_VENDOR_SPEC) {
+
+ /* Unexpected default class combined with differing interface class */
+ SHOW_PROGRESS(output,"Bogus Class/InterfaceClass: 0x%02x/0x08\n", defaultClass);
+ defaultClass = 8;
+ }
+
+ if ((strlen(MessageContent) && strncmp("55534243",MessageContent,8) == 0)
+ || StandardEject || ModeMap & HUAWEINEW_MODE || ModeMap & HUAWEIALT_MODE
+ || ModeMap & CISCO_MODE || ModeMap & OPTION_MODE)
+ if (defaultClass != 8) {
+ fprintf(stderr, "Error: can't use storage command in MessageContent with interface %d; "
+ "interface class is %d, expected 8. Abort\n\n", Interface, defaultClass);
+ abortExit();
+ }
+
+ /* Check or get endpoints and alloc message list if needed*/
+ if (strlen(MessageContent) || StandardEject || ModeMap & CISCO_MODE
+ || ModeMap & HUAWEINEW_MODE || ModeMap & HUAWEIALT_MODE
+ || ModeMap & OPTION_MODE) {
+
+ Messages = (char**) calloc(MSG_DIM, sizeof(char*));
+ for (i = 0; i < MSG_DIM; i++) {
+ Messages[i] = (char*) calloc(LINE_DIM, sizeof(char));
+ Messages[i][0] = '\0';
+ }
+
+ if (!MessageEndpoint)
+ MessageEndpoint = find_first_bulk_endpoint(LIBUSB_ENDPOINT_OUT);
+ if (!ResponseEndpoint)
+ ResponseEndpoint = find_first_bulk_endpoint(LIBUSB_ENDPOINT_IN);
+ if (!MessageEndpoint) {
+ fprintf(stderr,"Error: message endpoint not given or found. Abort\n\n");
+ abortExit();
+ }
+ if (!ResponseEndpoint) {
+ fprintf(stderr,"Error: response endpoint not given or found. Abort\n\n");
+ abortExit();
+ }
+ SHOW_PROGRESS(output,"Use endpoints 0x%02x (out) and 0x%02x (in)\n", MessageEndpoint,
+ ResponseEndpoint);
+
+ }
+
+ if (verbose) {
+ fprintf(output,"\nUSB description data (for identification)\n");
+ deviceDescription();
+ }
+
+ /* Special modes are exclusive, so check for illegal combinations.
+ * More than one bit set?
+ */
+ if ( ModeMap & (ModeMap-1) ) {
+ fprintf(output,"Multiple special modes selected; check configuration. Abort\n\n");
+ abortExit();
+ }
+
+ if ((strlen(MessageContent) || StandardEject) && ModeMap ) {
+ MessageContent[0] = '\0';
+ StandardEject = 0;
+ fprintf(output,"Warning: MessageContent/StandardEject ignored; can't combine with special mode\n");
+ }
+
+ if (StandardEject && (strlen(MessageContent2) || strlen(MessageContent3))) {
+ fprintf(output,"Warning: MessageContent2/3 ignored; only one allowed with StandardEject\n");
+ }
+
+ if ( !ModeMap && !strlen(MessageContent) && AltSetting == -1 && !Configuration && !StandardEject )
+ SHOW_PROGRESS(output,"Warning: no switching method given. See documentation\n");
+
+ /*
+ * The switching actions
+ */
+
+ if (sysmode) {
+ openlog("usb_modeswitch", 0, LOG_SYSLOG);
+ syslog(LOG_NOTICE, "switch device %04x:%04x on %03d/%03d", DefaultVendor, DefaultProduct,
+ busnum, devnum);
+
+ }
+
+ if (ModeMap & DETACHONLY_MODE) {
+ SHOW_PROGRESS(output,"Detach storage driver as switching method ...\n");
+ ret = detachDrivers();
+ if (ret == 2)
+ SHOW_PROGRESS(output," You may want to remove the storage driver manually\n");
+ }
+
+ if(ModeMap & HUAWEI_MODE) {
+ switchHuaweiMode();
+ }
+ if(ModeMap & SIERRA_MODE) {
+ switchSierraMode();
+ }
+ if(ModeMap & GCT_MODE) {
+ detachDrivers();
+ switchGCTMode();
+ }
+ if(ModeMap & QISDA_MODE) {
+ switchQisdaMode();
+ }
+ if(ModeMap & KOBIL_MODE) {
+ detachDrivers();
+ switchKobilMode();
+ }
+ if(ModeMap & QUANTA_MODE) {
+ switchQuantaMode();
+ }
+ if(ModeMap & SEQUANS_MODE) {
+ switchSequansMode();
+ }
+ if(ModeMap & MOBILEACTION_MODE) {
+ switchActionMode();
+ }
+ if(ModeMap & CISCO_MODE) {
+ detachDrivers();
+ switchCiscoMode();
+ }
+ if(ModeMap & BLACKBERRY_MODE) {
+ detachDrivers();
+ switchBlackberryMode();
+ }
+ if(ModeMap & PANTECH_MODE) {
+ detachDrivers();
+ if (PantechMode > 1)
+ switchPantechMode();
+ else
+ SHOW_PROGRESS(output,"Waiting for auto-switch of Pantech modem ...\n");
+ }
+ if(ModeMap & SONY_MODE) {
+ if (CheckSuccess)
+ SHOW_PROGRESS(output,"Note: CheckSuccess ignored; Sony mode does separate checks\n");
+ CheckSuccess = 0; /* separate and implied success control */
+ sonySuccess = switchSonyMode();
+ }
+
+ if (StandardEject) {
+ SHOW_PROGRESS(output,"Sending standard EJECT sequence\n");
+ detachDrivers();
+
+ strcpy(Messages[0],"5553424387654321000000000000061e000000000000000000000000000000");
+ strcpy(Messages[1],"5553424397654321000000000000061b000000020000000000000000000000");
+ strcpy(Messages[2],"5553424387654321000000000001061e000000000000000000000000000000");
+ strcpy(Messages[3],"5553424397654321000000000001061b000000020000000000000000000000");
+ if (MessageContent[0] != '\0')
+ strcpy(Messages[4], MessageContent);
+
+ switchSendMessage();
+ } else if (ModeMap & HUAWEINEW_MODE) {
+ SHOW_PROGRESS(output,"Using standard Huawei switching message\n");
+ detachDrivers();
+ strcpy(Messages[0],"55534243123456780000000000000011062000000101000100000000000000");
+ switchSendMessage();
+ } else if (ModeMap & HUAWEIALT_MODE) {
+ SHOW_PROGRESS(output,"Using alternative Huawei switching message\n");
+ detachDrivers();
+ strcpy(Messages[0],"55534243123456780000000000000011063000000000010000000000000000");
+ switchSendMessage();
+ } else if (ModeMap & OPTION_MODE) {
+ SHOW_PROGRESS(output,"Using standard Option switching message\n");
+ detachDrivers();
+ strcpy(Messages[0],"55534243123456780000000000000601000000000000000000000000000000");
+ switchSendMessage();
+ } else if (strlen(MessageContent)) {
+ detachDrivers();
+ strcpy(Messages[0],MessageContent);
+ if (MessageContent2[0] != '\0')
+ strcpy(Messages[1], MessageContent2);
+ if (MessageContent3[0] != '\0')
+ strcpy(Messages[2], MessageContent3);
+ switchSendMessage();
+ }
+
+ if (Configuration > 0) {
+ if (currentConfigVal != Configuration) {
+ if (switchConfiguration()) {
+ currentConfigVal = get_current_config_value(dev);
+ if (currentConfigVal == Configuration) {
+ SHOW_PROGRESS(output,"The configuration was set successfully\n");
+ } else {
+ SHOW_PROGRESS(output,"Changing the configuration has failed\n");
+ }
+ }
+ } else {
+ SHOW_PROGRESS(output,"Target configuration %d already active. Nothing to do. Bye!\n\n", currentConfigVal);
+ close_all();
+ exit(0);
+ }
+ }
+
+ if (AltSetting != -1) {
+ switchAltSetting();
+ }
+
+ /* No "removal" check if these are set */
+ if ((Configuration > 0 || AltSetting > -1) && !ResetUSB) {
+ libusb_close(devh);
+ devh = NULL;
+ }
+
+ if (ResetUSB) {
+ resetUSB();
+ devh = NULL;
+ }
+
+ if (searchMode == SEARCH_BUSDEV && sysmode) {
+ printf("ok:busdev\n");
+ close_all();
+ exit(0);
+ }
+
+ if (CheckSuccess) {
+ if (checkSuccess()) {
+ if (sysmode) {
+ if (NoDriverLoading)
+ printf("ok:\n");
+ else
+ if (TargetProduct < 1)
+ printf("ok:no_data\n");
+ else
+ printf("ok:%04x:%04x\n", TargetVendor, TargetProduct);
+ }
+ } else
+ if (sysmode)
+ printf("fail:\n");
+ } else {
+ if (ModeMap & SONY_MODE)
+ if (sonySuccess) {
+ if (sysmode) {
+ syslog(LOG_NOTICE, "switched S.E. MD400 to modem mode");
+ printf("ok:\n"); /* ACM device, no driver action */
+ }
+ SHOW_PROGRESS(output,"-> device should be stable now. Bye!\n\n");
+ } else {
+ if (sysmode)
+ printf("fail:\n");
+ SHOW_PROGRESS(output,"-> switching was probably not completed. Bye!\n\n");
+ }
+ else
+ SHOW_PROGRESS(output,"-> Run lsusb to note any changes. Bye!\n\n");
+ }
+ close_all();
+ exit(0);
+}
+
+
+/* Get descriptor strings if available (identification details) */
+void deviceDescription ()
+{
+ char imanufact[DESCR_MAX], iproduct[DESCR_MAX], iserial[DESCR_MAX];
+ int ret=0;
+ char* c;
+ memset (imanufact, ' ', DESCR_MAX);
+ memset (iproduct, ' ', DESCR_MAX);
+ memset (iserial, ' ', DESCR_MAX);
+
+ struct libusb_device_descriptor descriptor;
+ libusb_get_device_descriptor(dev, &descriptor);
+
+ int iManufacturer = descriptor.iManufacturer;
+ int iProduct = descriptor.iProduct;
+ int iSerialNumber = descriptor.iSerialNumber;
+
+ if (iManufacturer) {
+ ret = libusb_get_string_descriptor_ascii(devh, iManufacturer, (unsigned char *)imanufact, DESCR_MAX);
+ if (ret < 0) {
+ fprintf(stderr, "Error: could not get description string \"manufacturer\"\n");
+ strcpy(imanufact, "read error");
+ }
+ } else
+ strcpy(imanufact, "not provided");
+ c = strstr(imanufact, " ");
+ if (c)
+ memset((void*)c, '\0', 1);
+
+ if (iProduct) {
+ ret = libusb_get_string_descriptor_ascii(devh, iProduct, (unsigned char *)iproduct, DESCR_MAX);
+ if (ret < 0) {
+ fprintf(stderr, "Error: could not get description string \"product\"\n");
+ strcpy(iproduct, "read error");
+ }
+ } else
+ strcpy(iproduct, "not provided");
+ c = strstr(iproduct, " ");
+ if (c)
+ memset((void*)c, '\0', 1);
+
+ if (iSerialNumber) {
+ ret = libusb_get_string_descriptor_ascii(devh, iSerialNumber, (unsigned char *)iserial, DESCR_MAX);
+ if (ret < 0) {
+ fprintf(stderr, "Error: could not get description string \"serial number\"\n");
+ strcpy(iserial, "read error");
+ }
+ } else
+ strcpy(iserial, "not provided");
+ c = strstr(iserial, " ");
+ if (c)
+ memset((void*)c, '\0', 1);
+ fprintf(output,"-------------------------\n");
+ fprintf(output,"Manufacturer: %s\n", imanufact);
+ fprintf(output," Product: %s\n", iproduct);
+ fprintf(output," Serial No.: %s\n", iserial);
+ fprintf(output,"-------------------------\n");
+}
+
+
+/* Auxiliary function used by the wrapper */
+int findMBIMConfig(int vendor, int product, int mode)
+{
+ struct libusb_device **devs;
+ int resultConfig=0;
+ int i=0, j;
+
+ if (libusb_get_device_list(ctx, &devs) < 0) {
+ perror("Libusb could not access USB. Abort");
+ return 0;
+ }
+
+ SHOW_PROGRESS(output,"Search USB devices ...\n");
+ while ((dev = devs[i++]) != NULL) {
+ struct libusb_device_descriptor descriptor;
+ libusb_get_device_descriptor(dev, &descriptor);
+
+ if (mode == SEARCH_BUSDEV) {
+ if ((libusb_get_bus_number(dev) != busnum) ||
+ (libusb_get_device_address(dev) != devnum)) {
+ continue;
+ } else {
+ if (descriptor.idVendor != vendor)
+ continue;
+ if (product != descriptor.idProduct)
+ continue;
+ }
+ }
+ SHOW_PROGRESS(output,"Found device, search for MBIM configuration...\n");
+
+ // No check if there is only one configuration
+ if (descriptor.bNumConfigurations < 2)
+ return -1;
+
+ // Checking all interfaces of all configurations
+ for (j=0; j<descriptor.bNumConfigurations; j++) {
+ struct libusb_config_descriptor *config;
+
+ libusb_get_config_descriptor(dev, j, &config);
+ resultConfig = config->bConfigurationValue;
+ for (i=0; i<config->bNumInterfaces; i++) {
+ if ( config->interface[i].altsetting[0].bInterfaceClass == 2 )
+ if ( config->interface[i].altsetting[0].bInterfaceSubClass == 0x0e ) {
+ // found MBIM interface in this configuration
+ libusb_free_config_descriptor(config);
+ return resultConfig;
+ }
+ }
+ libusb_free_config_descriptor(config);
+ }
+ return -1;
+ }
+ return 0;
+}
+
+
+void resetUSB ()
+{
+ int success;
+ int bpoint = 0;
+
+ if (!devh) {
+ fprintf(output,"Device handle empty, skip USB reset\n");
+ return;
+ }
+ if (show_progress) {
+ fprintf(output,"Reset USB device ");
+ fflush(output);
+ }
+ sleep( 1 );
+ do {
+ success = libusb_reset_device(devh);
+ if ( ((bpoint % 10) == 0) && show_progress ) {
+ fprintf(output,".");
+ fflush(output);
+ }
+ bpoint++;
+ if (bpoint > 100)
+ success = 1;
+ } while (success < 0);
+
+ if ( success ) {
+ SHOW_PROGRESS(output,"\n Device reset failed.\n");
+ } else
+ SHOW_PROGRESS(output,"\n Device was reset\n");
+}
+
+
+int switchSendMessage ()
+{
+ const char* cmdHead = "55534243";
+ int ret, i;
+ int retries = 1;
+/* char* msg[3];
+ msg[0] = MessageContent;
+ msg[1] = MessageContent2;
+ msg[2] = MessageContent3;
+*/
+ SHOW_PROGRESS(output,"Set up interface %d\n", Interface);
+ ret = libusb_claim_interface(devh, Interface);
+ if (ret != 0) {
+ SHOW_PROGRESS(output," Could not claim interface (error %d). Skip message sending\n", ret);
+ return 0;
+ }
+
+ SHOW_PROGRESS(output,"Use endpoint 0x%02x for message sending ...\n", MessageEndpoint);
+ if (show_progress)
+ fflush(stdout);
+
+retry:
+ for (i=0; i<MSG_DIM; i++) {
+ if ( strlen(Messages[i]) == 0)
+ break;
+
+ if ( sendMessage(Messages[i], i+1) )
+ goto skip;
+
+ if ( strstr(Messages[i],cmdHead) != NULL ) {
+ // UFI command
+ SHOW_PROGRESS(output,"Read the response to message %d (CSW) ...\n", i+1);
+ ret = read_bulk(ResponseEndpoint, ByteString, 13);
+ if (ret >= 0) {
+ SHOW_PROGRESS(output,", status %d",ByteString[12]);
+ }
+ } /* else {
+ // Other bulk transfer
+ SHOW_PROGRESS(output,"Read the response to message %d ...\n", i+1);
+ ret = read_bulk(ResponseEndpoint, ByteString, strlen(Messages[i])/2 );
+ }*/
+ SHOW_PROGRESS(output,"\n");
+ if (ret == LIBUSB_TRANSFER_STALL && retries--) {
+ SHOW_PROGRESS(output,"Endpoint stalled. Resetting ...\n");
+ libusb_clear_halt(devh, MessageEndpoint);
+ goto retry;
+ }
+ if (ret < 0)
+ goto skip;
+ }
+
+ SHOW_PROGRESS(output,"Reset response endpoint 0x%02x\n", ResponseEndpoint);
+ ret = libusb_clear_halt(devh, ResponseEndpoint);
+ if (ret)
+ SHOW_PROGRESS(output," Could not reset endpoint (probably harmless): %d\n", ret);
+ SHOW_PROGRESS(output,"Reset message endpoint 0x%02x\n", MessageEndpoint);
+ ret = libusb_clear_halt(devh, MessageEndpoint);
+ if (ret)
+ SHOW_PROGRESS(output," Could not reset endpoint (probably harmless): %d\n", ret);
+ usleep(50000);
+
+ if (ReleaseDelay) {
+ SHOW_PROGRESS(output,"Wait for %d ms before releasing interface ...\n", ReleaseDelay);
+ usleep(ReleaseDelay*1000);
+ }
+ ret = libusb_release_interface(devh, Interface);
+ if (ret)
+ goto skip;
+ return 1;
+
+skip:
+ SHOW_PROGRESS(output," Device is gone, skip any further commands\n");
+ libusb_close(devh);
+ devh = NULL;
+ return 2;
+}
+
+
+int switchConfiguration ()
+{
+ int ret;
+
+ SHOW_PROGRESS(output,"Change configuration to %i ...\n", Configuration);
+ detachDrivers();
+ ret = libusb_set_configuration(devh, -1);
+ if (ret < 0) {
+ SHOW_PROGRESS(output," Resetting the configuration failed (error %d). Try to continue\n", ret);
+ }
+ /* Empirically tested wait period, improves reliability of configuration change */
+ usleep(100000);
+ ret = libusb_set_configuration(devh, Configuration);
+ if (ret < 0) {
+ SHOW_PROGRESS(output," Changing the configuration failed (error %d). Try to continue\n", ret);
+ return 0;
+ } else {
+ SHOW_PROGRESS(output," OK, configuration set\n");
+ return 1;
+ }
+}
+
+
+int switchAltSetting ()
+{
+ int ret;
+ SHOW_PROGRESS(output,"Change to alt setting %i ...\n", AltSetting);
+ ret = libusb_claim_interface(devh, Interface);
+ if (ret < 0) {
+ SHOW_PROGRESS(output," Could not claim interface (error %d). Skip AltSetting\n", ret);
+ return 0;
+ }
+ ret = libusb_set_interface_alt_setting(devh, Interface, AltSetting);
+ libusb_release_interface(devh, Interface);
+ if (ret < 0) {
+ SHOW_PROGRESS(output," Change to alt setting returned error %d. Try to continue\n", ret);
+ return 0;
+ } else
+ return 1;
+}
+
+
+void switchHuaweiMode ()
+{
+ int ret;
+ SHOW_PROGRESS(output,"Send old Huawei control message ...\n");
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
+ LIBUSB_REQUEST_SET_FEATURE, 00000001, 0, buffer, 0, 1000);
+
+ if (ret != 0) {
+ fprintf(stderr, "Error: Huawei control message failed (error %d). Abort\n\n", ret);
+ exit(0);
+ }
+}
+
+
+void switchSierraMode ()
+{
+ int ret;
+ SHOW_PROGRESS(output,"Send Sierra control message\n");
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
+ LIBUSB_REQUEST_SET_INTERFACE, 00000001, 0, buffer, 0, 1000);
+ if (ret == LIBUSB_ERROR_PIPE) {
+ SHOW_PROGRESS(output," communication with device stopped. May have switched modes anyway\n");
+ return;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "Error: Sierra control message failed (error %d). Abort\n\n", ret);
+ exit(0);
+ }
+}
+
+
+void switchGCTMode ()
+{
+ int ret;
+ ret = libusb_claim_interface(devh, Interface);
+ if (ret != 0) {
+ SHOW_PROGRESS(output," Could not claim interface (error %d). Skip GCT sequence\n", ret);
+ return;
+ }
+ SHOW_PROGRESS(output,"Send GCT control message 1 ...\n type (should be 161/0xA1): %d",
+ LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN);
+
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,
+ 0xa0, 0, Interface, buffer, 1, 1000);
+
+ if (ret < 0) {
+ SHOW_PROGRESS(output," GCT control message 1 failed (error %d), continue anyway ...\n", ret);
+ }
+ SHOW_PROGRESS(output,"Send GCT control message 2 ...\n");
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,
+ 0xfe, 0, Interface, buffer, 1, 1000);
+
+ if (ret < 0) {
+ SHOW_PROGRESS(output," GCT control message 2 failed (error %d). Abort\n\n", ret);
+ }
+ libusb_release_interface(devh, Interface);
+ if (ret < 0)
+ exit(0);
+}
+
+
+void switchKobilMode() {
+ int ret;
+ SHOW_PROGRESS(output,"Send Kobil control message ...\n");
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN,
+ 0x88, 0, 0, buffer, 8, 1000);
+
+ if (ret < 0) {
+ fprintf(stderr, "Error: Kobil control message failed (error %d). Abort\n\n", ret);
+ exit(0);
+ }
+}
+
+
+void switchQisdaMode () {
+ int ret;
+ SHOW_PROGRESS(output,"Sending Qisda control message ...\n");
+ memcpy(buffer, "\x05\x8c\x04\x08\xa0\xee\x20\x00\x5c\x01\x04\x08\x98\xcd\xea\xbf", 16);
+ ret = libusb_control_transfer(devh, 0x40, 0x04, 0, 0, buffer, 16, 1000);
+ if (ret < 0) {
+ fprintf(stderr, "Error: Qisda control message failed (error %d). Abort\n\n", ret);
+ exit(0);
+ }
+}
+
+
+void switchQuantaMode() {
+ int ret;
+ SHOW_PROGRESS(output,"Send Quanta control message ...\n");
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN,
+ 0xff, 0, 0, buffer, 0, 1000);
+
+ if (ret < 0) {
+ SHOW_PROGRESS(output,"Error: Quanta control message failed (error %d). Abort\n\n", ret);
+ exit(0);
+ }
+}
+
+
+void switchBlackberryMode ()
+{
+ int ret;
+ SHOW_PROGRESS(output,"Send Blackberry control message 1 ...\n");
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN,
+ 0xb1, 0x0000, 0, buffer, 8, 1000);
+
+ if (ret != 8) {
+ fprintf(stderr, "Error: Blackberry control message 1 failed (result %d)\n", ret);
+ }
+ SHOW_PROGRESS(output,"Send Blackberry control message 2 ...\n");
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN,
+ 0xa9, 0x000e, 0, buffer, 2, 1000);
+
+ if (ret != 2) {
+ fprintf(stderr, "Error: Blackberry control message 2 failed (result %d). Abort\n\n", ret);
+ exit(0);
+ }
+}
+
+
+void switchPantechMode()
+{
+ int ret;
+ SHOW_PROGRESS(output,"Send Pantech control message, wValue %d ...\n", PantechMode);
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT,
+ 0x70, PantechMode, 0, buffer, 0, 1000);
+
+ if (ret < 0) {
+ SHOW_PROGRESS(output," Error: Pantech control message failed (error %d). Abort\n\n", ret);
+ exit(0);
+ }
+}
+
+
+#define EP_OUT 0x02
+#define EP_IN 0x81
+#define SIZE 0x08
+
+#define MOBILE_ACTION_READLOOP1 63
+#define MOBILE_ACTION_READLOOP2 73
+
+/* The code here is statically derived from sniffing (and confirmed working).
+ * However, I bet it could be simplified significantly.
+ */
+
+void switchActionMode ()
+{
+ int ret, i;
+ SHOW_PROGRESS(output,"Send MobileAction control sequence ...\n");
+ memcpy(buffer, "\xb0\x04\x00\x00\x02\x90\x26\x86", SIZE);
+ libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
+ 0x09, 0x0300, 0, buffer, SIZE, 1000);
+
+ memcpy(buffer, "\xb0\x04\x00\x00\x02\x90\x26\x86", SIZE);
+ libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
+ 0x09, 0x0300, 0, buffer, SIZE, 1000);
+
+ usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000);
+ usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000);
+ memcpy(buffer, "\x37\x01\xfe\xdb\xc1\x33\x1f\x83", SIZE);
+ usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000);
+ usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000);
+ memcpy(buffer, "\x37\x0e\xb5\x9d\x3b\x8a\x91\x51", SIZE);
+ usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000);
+ usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000);
+ memcpy(buffer, "\x34\x87\xba\x0d\xfc\x8a\x91\x51", SIZE);
+ usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000);
+ for (i=0; i < MOBILE_ACTION_READLOOP1; i++) {
+ usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000);
+ }
+ memcpy(buffer, "\x37\x01\xfe\xdb\xc1\x33\x1f\x83", SIZE);
+ usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000);
+ usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000);
+ memcpy(buffer, "\x37\x0e\xb5\x9d\x3b\x8a\x91\x51", SIZE);
+ usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000);
+ usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000);
+ memcpy(buffer, "\x34\x87\xba\x0d\xfc\x8a\x91\x51", SIZE);
+ usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000);
+ for (i=0; i < MOBILE_ACTION_READLOOP2; i++) {
+ usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000);
+ }
+ memcpy(buffer, "\x33\x04\xfe\x00\xf4\x6c\x1f\xf0", SIZE);
+ usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000);
+ usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000);
+ memcpy(buffer, "\x32\x07\xfe\xf0\x29\xb9\x3a\xf0", SIZE);
+ ret = usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000);
+ usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000);
+ if (ret < 0) {
+ SHOW_PROGRESS(output," MobileAction control sequence did not complete\n"
+ " Last error was %d\n",ret);
+ } else {
+ SHOW_PROGRESS(output," MobileAction control sequence complete\n");
+ }
+}
+
+
+#define SQN_SET_DEVICE_MODE_REQUEST 0x0b
+#define SQN_GET_DEVICE_MODE_REQUEST 0x0a
+
+#define SQN_DEFAULT_DEVICE_MODE 0x00
+#define SQN_MASS_STORAGE_MODE 0x01
+#define SQN_CUSTOM_DEVICE_MODE 0x02
+
+void switchSequansMode()
+{
+
+ int ret;
+ SHOW_PROGRESS(output,"Send Sequans control message\n");
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE
+ | LIBUSB_ENDPOINT_OUT, SQN_SET_DEVICE_MODE_REQUEST, SQN_CUSTOM_DEVICE_MODE,
+ 0, buffer, 0, 1000);
+
+ if (ret < 0) {
+ fprintf(stderr, "Error: Sequans request failed (error %d). Abort\n\n", ret);
+ exit(0);
+ }
+}
+
+
+void switchCiscoMode()
+{
+ int ret, i, j;
+
+ strcpy(Messages[0],"55534243f83bcd810002000080000afd000000030000000100000000000000");
+ strcpy(Messages[1],"55534243984300820002000080000afd000000070000000100000000000000");
+ strcpy(Messages[2],"55534243984300820000000000000afd000100071000000000000000000000");
+ strcpy(Messages[3],"55534243984300820002000080000afd000200230000000100000000000000");
+ strcpy(Messages[4],"55534243984300820000000000000afd000300238200000000000000000000");
+ strcpy(Messages[5],"55534243984300820002000080000afd000200260000000100000000000000");
+ strcpy(Messages[6],"55534243984300820000000000000afd00030026c800000000000000000000");
+ strcpy(Messages[7],"55534243d84c04820002000080000afd000010730000000100000000000000");
+ strcpy(Messages[8],"55534243d84c04820002000080000afd000200240000000100000000000000");
+ strcpy(Messages[9],"55534243d84c04820000000000000afd000300241300000000000000000000");
+ strcpy(Messages[10],"55534243d84c04820000000000000afd000110732400000000000000000000");
+
+ SHOW_PROGRESS(output,"Set up Cisco interface %d\n", Interface);
+ ret = libusb_claim_interface(devh, Interface);
+ if (ret < 0) {
+ SHOW_PROGRESS(output," Could not claim interface (error %d). Abort\n", ret);
+ abortExit();
+ }
+ if (show_progress)
+ fflush(output);
+
+// ret = read_bulk(ResponseEndpoint, ByteString, 13);
+// SHOW_PROGRESS(output," Extra response (CSW) read, result %d\n",ret);
+
+ for (i=0; i<11; i++) {
+ if ( sendMessage(Messages[i], i+1) )
+ goto skip;
+
+ for (j=1; j<4; j++) {
+
+ SHOW_PROGRESS(output," Read the CSW for bulk message %d (attempt %d) ...\n",i+1,j);
+ ret = read_bulk(ResponseEndpoint, ByteString, 13);
+ SHOW_PROGRESS(output,"\n");
+
+ if (ret < 0)
+ goto skip;
+ if (ret == 13)
+ break;
+ }
+ }
+ libusb_clear_halt(devh, MessageEndpoint);
+ libusb_clear_halt(devh, ResponseEndpoint);
+
+ ReleaseDelay = 2000;
+ SHOW_PROGRESS(output,"Wait for %d ms before releasing interface ...\n", ReleaseDelay);
+ usleep(ReleaseDelay*1000);
+
+ ret = libusb_release_interface(devh, Interface);
+ if (ret < 0)
+ goto skip;
+ return;
+
+skip:
+ SHOW_PROGRESS(output,"Device returned error %d, skip further commands\n", ret);
+ libusb_close(devh);
+ devh = NULL;
+}
+
+
+int switchSonyMode ()
+{
+ int ret, i, found;
+ detachDrivers();
+
+ if (CheckSuccess) {
+ CheckSuccess = 0;
+ }
+
+ SHOW_PROGRESS(output,"Send Sony control message\n");
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE
+ | LIBUSB_ENDPOINT_IN, 0x11, 2, 0, buffer, 3, 100);
+
+ if (ret < 0) {
+ fprintf(stderr, "Error: Sony control message failed (error %d). Abort\n\n", ret);
+ exit(0);
+ } else
+ SHOW_PROGRESS(output," OK, control message sent, wait for device to return ...\n");
+
+ libusb_close(devh);
+ devh = NULL;
+
+ /* Now waiting for the device to reappear */
+ devnum=-1;
+ busnum=-1;
+ i=0;
+ dev = 0;
+ while ( dev == 0 && i < 30 ) {
+ if ( i > 5 ) {
+ dev = search_devices(&found, DefaultVendor, DefaultProductList, TargetClass,
+ 0, SEARCH_TARGET);
+ }
+ if ( dev != 0 )
+ break;
+ sleep(1);
+ if (show_progress) {
+ fprintf(output,"#");
+ fflush(stdout);
+ }
+ i++;
+ }
+ SHOW_PROGRESS(output,"\n After %d seconds:",i);
+ if ( dev ) {
+ SHOW_PROGRESS(output," device came back, proceed\n");
+ libusb_open(dev, &devh);
+ if (devh == 0) {
+ fprintf(stderr, "Error: could not get handle on device\n");
+ return 0;
+ }
+ } else {
+ SHOW_PROGRESS(output," device still gone, abort\n");
+ return 0;
+ }
+ sleep(1);
+
+ SHOW_PROGRESS(output,"Send Sony control message again ...\n");
+ ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE
+ | LIBUSB_ENDPOINT_IN, 0x11, 2, 0, buffer, 3, 100);
+
+ if (ret < 0) {
+ fprintf(stderr, "Error: Sony control message (2) failed (error %d)\n", ret);
+ return 0;
+ }
+ SHOW_PROGRESS(output," OK, control message sent\n");
+ return 1;
+}
+
+
+/* Detach driver
+ */
+int detachDrivers()
+{
+ int i, ret;
+ SHOW_PROGRESS(output,"Looking for active drivers ...\n");
+ ret = libusb_kernel_driver_active(devh, 0);
+ if (ret == LIBUSB_ERROR_NOT_SUPPORTED) {
+ fprintf(output," Can't do driver detection on this platform.\n");
+ return 2;
+ }
+
+ struct libusb_config_descriptor *config;
+ libusb_get_active_config_descriptor(dev, &config);
+
+ for (i=0; i<config->bNumInterfaces; i++) {
+ ret = libusb_kernel_driver_active(devh, i);
+ if (ret < 0) {
+ SHOW_PROGRESS(output," Failed to check driver status for interface %d (error %d)\n Try to continue\n",i,ret);
+ continue;
+ }
+ if (ret) {
+ ret = libusb_detach_kernel_driver(devh, i);
+ if (ret == LIBUSB_ERROR_NOT_SUPPORTED) {
+ fprintf(output," Can't do driver detaching on this platform.\n");
+ return 2;
+ }
+ if (ret == 0) {
+ SHOW_PROGRESS(output," OK, driver detached\n");
+ } else {
+ SHOW_PROGRESS(output," Driver detach failed for interface %d (error %d).\n Try to continue\n",i,ret);
+ continue;
+ }
+ }
+ }
+ libusb_free_config_descriptor(config);
+ return 1;
+}
+
+
+int sendMessage(char* message, int count)
+{
+ int ret, message_length;
+
+ if (strlen(message) % 2 != 0) {
+ fprintf(stderr, "Error: MessageContent %d hex string has uneven length. Skipping ...\n", count);
+ return 1;
+ }
+ message_length = strlen(message) / 2;
+ if ( hexstr2bin(message, ByteString, message_length) == -1) {
+ fprintf(stderr, "Error: MessageContent %d %s\n is not a hex string. Skipping ...\n",
+ count, MessageContent);
+
+ return 1;
+ }
+ SHOW_PROGRESS(output,"Trying to send message %d to endpoint 0x%02x ...\n", count, MessageEndpoint);
+ fflush(output);
+ ret = write_bulk(MessageEndpoint, ByteString, message_length);
+ if (ret == LIBUSB_ERROR_NO_DEVICE)
+ return 1;
+
+ return 0;
+}
+
+
+int checkSuccess()
+{
+ int ret, i;
+ int newTargetCount, success=0;
+
+ SHOW_PROGRESS(output,"\nCheck for mode switch (max. %d times, once per second) ...\n", CheckSuccess);
+ sleep(1);
+
+ /* If target parameters are given, don't check for vanished device
+ * Changed for Cisco AM10 where a new device is added while the install
+ * storage device stays active
+ */
+ if ((TargetVendor || TargetClass) && devh) {
+ libusb_close(devh);
+ devh = NULL;
+ }
+
+ /* if target ID is not given but target class is, assign default as target;
+ * it will be needed for sysmode output
+ */
+ if (!TargetVendor && TargetClass) {
+ TargetVendor = DefaultVendor;
+ TargetProduct = DefaultProduct;
+ }
+
+ /* devh is 0 if device vanished during command transmission or if target params were given
+ */
+ if (devh)
+ for (i=0; i < CheckSuccess; i++) {
+
+ /* Test if default device still can be accessed; positive result does
+ * not necessarily mean failure
+ */
+ SHOW_PROGRESS(output," Wait for original device to vanish ...\n");
+
+ ret = libusb_claim_interface(devh, Interface);
+ libusb_release_interface(devh, Interface);
+ if (ret < 0) {
+ SHOW_PROGRESS(output," Original device can't be accessed anymore. Good.\n");
+ libusb_close(devh);
+ devh = NULL;
+ break;
+ }
+ if (i == CheckSuccess-1) {
+ SHOW_PROGRESS(output," Original device still present after the timeout\n\n"
+ "Mode switch most likely failed. Bye!\n\n");
+ } else
+ sleep(1);
+ }
+
+ if ( TargetVendor && (TargetProduct > -1 || TargetProductList[0] != '\0') ) {
+
+ /* Recount target devices (compare with previous count) if target data is given.
+ * Target device on the same bus with higher device number is returned,
+ * description is read for syslog message
+ */
+ // Wait counter passed on from previous loop
+ for (i=i; i < CheckSuccess; i++) {
+ SHOW_PROGRESS(output," Search for target devices ...\n");
+ dev = search_devices(&newTargetCount, TargetVendor, TargetProductList,
+ TargetClass, 0, SEARCH_TARGET);
+
+ if (dev && (newTargetCount > targetDeviceCount)) {
+ if (verbose) {
+ libusb_open(dev, &devh);
+ fprintf(output,"\nFound target device %03d on bus %03d\n",
+ libusb_get_device_address(dev), libusb_get_bus_number(dev));
+
+ fprintf(output,"\nTarget device description data\n");
+ deviceDescription();
+ libusb_close(devh);
+ devh = NULL;
+ }
+ SHOW_PROGRESS(output," Found correct target device\n\n"
+ "Mode switch succeeded. Bye!\n\n");
+
+ success = 2;
+ break;
+ }
+ if (i == CheckSuccess-1) {
+ SHOW_PROGRESS(output," No new devices in target mode or class found\n\n"
+ "Mode switch has failed. Bye!\n\n");
+ } else
+ sleep(1);
+ }
+ } else
+ /* No target data given, rely on the vanished device */
+ if (!devh) {
+ SHOW_PROGRESS(output," (For a better success check provide target IDs or class)\n");
+ SHOW_PROGRESS(output," Original device vanished after switching\n\n"
+ "Mode switch most likely succeeded. Bye!\n\n");
+ success = 1;
+ }
+
+ switch (success) {
+ case 2:
+ if (sysmode)
+ syslog(LOG_NOTICE, "switched to %04x:%04x on %03d/%03d", TargetVendor,
+ TargetProduct, busnum, devnum);
+
+ success = 1;
+ break;
+ case 1:
+ if (sysmode)
+ syslog(LOG_NOTICE, "device seems to have switched");
+ default:
+ ;
+ }
+ if (sysmode)
+ closelog();
+
+ return success;
+
+}
+
+
+int write_bulk(int endpoint, unsigned char *message, int length)
+{
+ int ret = usb_bulk_io(devh, endpoint, message, length, 3000);
+ if (ret >= 0 ) {
+ SHOW_PROGRESS(output," OK, message successfully sent\n");
+ } else
+ if (ret == LIBUSB_ERROR_NO_DEVICE) {
+ SHOW_PROGRESS(output," Device seems to have vanished right after sending. Good.\n");
+ } else
+ SHOW_PROGRESS(output," Sending the message returned error %d. Try to continue\n", ret);
+ return ret;
+}
+
+
+int read_bulk(int endpoint, unsigned char *buffer, int length)
+{
+ int ret = usb_bulk_io(devh, endpoint, buffer, length, 3000);
+ if (ret >= 0 ) {
+ SHOW_PROGRESS(output," Response successfully read (%d bytes)", ret);
+ } else
+ if (ret == LIBUSB_ERROR_NO_DEVICE) {
+ SHOW_PROGRESS(output," Device seems to have vanished after reading. Good.");
+ } else
+ SHOW_PROGRESS(output," Response reading failed (error %d)", ret);
+ return ret;
+}
+
+
+void release_usb_device(int __attribute__((unused)) dummy)
+{
+ SHOW_PROGRESS(output,"Program cancelled by system. Bye!\n\n");
+ if (devh)
+ libusb_release_interface(devh, Interface);
+ close_all();
+ exit(0);
+}
+
+
+/* Iterates over busses and devices, counts the ones which match the given
+ * parameters and returns the last one of them
+*/
+struct libusb_device* search_devices( int *numFound, int vendor, char* productList,
+ int targetClass, int configuration, int mode)
+{
+ char *listcopy=NULL, *token;
+ unsigned char buffer[2];
+ int devClass, product;
+ struct libusb_device* right_dev = NULL;
+ struct libusb_device **devs;
+ int i=0;
+
+ /* only target class given, target vendor and product assumed unchanged */
+ if ( targetClass && !(vendor || strlen(productList)) ) {
+ vendor = DefaultVendor;
+ productList = DefaultProductList;
+ }
+ *numFound = 0;
+
+ /* Sanity check */
+ if (!vendor || *productList == '\0')
+ return NULL;
+
+ listcopy = malloc(strlen(productList)+1);
+
+ if (libusb_get_device_list(ctx, &devs) < 0) {
+ perror("Libusb failed to get USB access!");
+ return 0;
+ }
+
+ while ((dev = devs[i++]) != NULL) {
+ struct libusb_device_descriptor descriptor;
+ libusb_get_device_descriptor(dev, &descriptor);
+
+ if (mode == SEARCH_BUSDEV) {
+ if ((libusb_get_bus_number(dev) != busnum) ||
+ (libusb_get_device_address(dev) != devnum))
+ continue;
+ else
+ SHOW_PROGRESS(output," bus/device number matched\n");
+ }
+
+ if (verbose)
+ fprintf (output," found USB ID %04x:%04x\n",
+ descriptor.idVendor, descriptor.idProduct);
+ if (descriptor.idVendor != vendor)
+ continue;
+ if (verbose)
+ fprintf (output," vendor ID matched\n");
+
+ strcpy(listcopy, productList);
+ token = strtok(listcopy, ",");
+ while (token != NULL) {
+ if (strlen(token) != 4) {
+ SHOW_PROGRESS(output,"Error: entry in product ID list has wrong length: %s. "
+ "Ignored\n", token);
+
+ goto NextToken;
+ }
+ if ( hexstr2bin(token, buffer, strlen(token)/2) == -1) {
+ SHOW_PROGRESS(output,"Error: entry in product ID list is not a hex string: %s. "
+ "Ignored\n", token);
+
+ goto NextToken;
+ }
+ product = 0;
+ product += (unsigned char)buffer[0];
+ product <<= 8;
+ product += (unsigned char)buffer[1];
+ if (product == descriptor.idProduct) {
+ if (verbose)
+ fprintf(output," product ID matched\n");
+
+ if (targetClass != 0) {
+ /* TargetClass is set, check class of first interface */
+ struct libusb_device_descriptor descriptor;
+ libusb_get_device_descriptor(dev, &descriptor);
+ devClass = descriptor.bDeviceClass;
+ struct libusb_config_descriptor *config;
+ libusb_get_config_descriptor(dev, 0, &config);
+ int ifaceClass = config->interface[0].altsetting[0].bInterfaceClass;
+ libusb_free_config_descriptor(config);
+ if (devClass == 0)
+ devClass = ifaceClass;
+ else
+ /* Check for some quirky devices */
+ if (devClass != ifaceClass)
+ devClass = ifaceClass;
+ if (devClass == targetClass) {
+ if (verbose)
+ fprintf (output," target class %02x matches\n", targetClass);
+ if (mode == SEARCH_TARGET) {
+ (*numFound)++;
+ right_dev = dev;
+ if (verbose)
+ fprintf (output," count device\n");
+ } else
+ if (verbose)
+ fprintf (output," device not counted, target class reached\n");
+ } else {
+ if (verbose)
+ fprintf (output," device class %02x not matching target\n", devClass);
+ if (mode == SEARCH_DEFAULT || mode == SEARCH_BUSDEV) {
+ (*numFound)++;
+ right_dev = dev;
+ if (verbose)
+ fprintf (output," count device\n");
+ }
+ }
+ } else {
+ /* Neither TargetClass nor Configuration are set */
+ (*numFound)++;
+ right_dev = dev;
+ if (mode == SEARCH_BUSDEV)
+ break;
+ }
+ }
+
+ NextToken:
+ token = strtok(NULL, ",");
+ }
+ }
+ if (listcopy != NULL)
+ free(listcopy);
+ return right_dev;
+}
+
+
+/* Autodetect bulk endpoints (ab) */
+
+int find_first_bulk_endpoint(int direction)
+{
+ int i, j;
+ const struct libusb_interface_descriptor *alt;
+ const struct libusb_endpoint_descriptor *ep;
+
+ for (j=0; j < active_config->bNumInterfaces; j++) {
+ alt = &(active_config->interface[j].altsetting[0]);
+ if (alt->bInterfaceNumber == Interface) {
+ for (i=0; i < alt->bNumEndpoints; i++) {
+ ep = &(alt->endpoint[i]);
+ if ( ( (ep->bmAttributes & LIBUSB_ENDPOINT_ADDRESS_MASK) == LIBUSB_TRANSFER_TYPE_BULK)
+ && ( (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == direction ) ) {
+
+ return ep->bEndpointAddress;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+int get_current_config_value()
+{
+ SHOW_PROGRESS(output,"Get the current device configuration ...\n");
+ if (active_config != NULL) {
+ libusb_free_config_descriptor(active_config);
+ active_config = NULL;
+ }
+ int ret = libusb_get_active_config_descriptor(dev, &active_config);
+ if (ret < 0) {
+ SHOW_PROGRESS(output," Determining the active configuration failed (error %d). Abort\n", ret);
+ abortExit();
+ }
+ return active_config->bConfigurationValue;
+}
+
+
+int get_interface_class()
+{
+ int i;
+ for (i=0; i < active_config->bNumInterfaces; i++) {
+ if (active_config->interface[i].altsetting[0].bInterfaceNumber == Interface)
+ return active_config->interface[i].altsetting[0].bInterfaceClass;
+ }
+ return -1;
+}
+
+
+/* Parameter parsing */
+
+char* ReadParseParam(const char* FileName, char *VariableName)
+{
+ static int numLines = 0;
+ static char* ConfigBuffer[MAXLINES];
+ char *VarName, *Comment=NULL, *Equal=NULL;
+ char *FirstQuote, *LastQuote, *P1, *P2;
+ int Line=0;
+ unsigned Len=0, Pos=0;
+ static char Str[LINE_DIM];
+ char *token, *configPos;
+ FILE *file = NULL;
+
+ // Reading and storing input during the first call
+ if (numLines==0) {
+ if (strncmp(FileName,"##",2) == 0) {
+ if (verbose) fprintf(output,"\nRead long config from command line\n");
+ // "Embedded" configuration data
+ configPos = (char*)FileName;
+ token = strtok(configPos, "\n");
+ strncpy(Str,token,LINE_DIM-1);
+ } else {
+ if (strcmp(FileName, "stdin")==0) {
+ if (verbose) fprintf(output,"\nRead long config from stdin\n");
+ file = stdin;
+ } else {
+ if (verbose) fprintf(output,"\nRead config file: %s\n", FileName);
+ file=fopen(FileName, "r");
+ }
+ if (file==NULL) {
+ fprintf(stderr, "Error: Could not find file %s. Abort\n\n", FileName);
+ abortExit();
+ } else {
+ token = fgets(Str, LINE_DIM-1, file);
+ }
+ }
+ while (token != NULL && numLines < MAXLINES) {
+ Len=strlen(Str);
+ if (Len==0)
+ goto NextLine;
+ if (Str[Len-1]=='\n' or Str[Len-1]=='\r')
+ Str[--Len]='\0';
+ Equal = strchr (Str, '='); // search for equal sign
+ Pos = strcspn (Str, ";#!"); // search for comment
+ Comment = (Pos==Len) ? NULL : Str+Pos;
+ if (Equal==NULL or ( Comment!=NULL and Comment<=Equal))
+ goto NextLine; // Comment or irrelevant, don't save
+ Len=strlen(Str)+1;
+ ConfigBuffer[numLines] = malloc(Len*sizeof(char));
+ strcpy(ConfigBuffer[numLines],Str);
+ numLines++;
+ NextLine:
+ if (file == NULL) {
+ token = strtok(NULL, "\n");
+ if (token != NULL)
+ strncpy(Str,token,LINE_DIM-1);
+ } else
+ token = fgets(Str, LINE_DIM-1, file);
+ }
+ if (file != NULL)
+ fclose(file);
+ }
+
+ // Now checking for parameters
+ Line=0;
+ while (Line < numLines) {
+ strcpy(Str,ConfigBuffer[Line]);
+ Equal = strchr (Str, '='); // search for equal sign
+ *Equal++ = '\0';
+
+ // String
+ FirstQuote=strchr (Equal, '"'); // search for double quote char
+ LastQuote=strrchr (Equal, '"');
+ if (FirstQuote!=NULL) {
+ if (LastQuote==NULL) {
+ fprintf(stderr, "Error reading parameters from file %s - "
+ "Missing end quote:\n%s\n", FileName, Str);
+
+ goto Next;
+ }
+ *FirstQuote=*LastQuote='\0';
+ Equal=FirstQuote+1;
+ }
+
+ // removes leading/trailing spaces
+ Pos=strspn (Str, " \t");
+ if (Pos==strlen(Str)) {
+ fprintf(stderr, "Error reading parameters from file %s - "
+ "Missing variable name:\n%s\n", FileName, Str);
+
+ goto Next;
+ }
+ while ((P1=strrchr(Str, ' '))!=NULL or (P2=strrchr(Str, '\t'))!=NULL)
+ if (P1!=NULL) *P1='\0';
+ else if (P2!=NULL) *P2='\0';
+ VarName=Str+Pos;
+
+ Pos=strspn (Equal, " \t");
+ if (Pos==strlen(Equal)) {
+ fprintf(stderr, "Error reading parameter from file %s - "
+ "Missing value:\n%s\n", FileName, Str);
+
+ goto Next;
+ }
+ Equal+=Pos;
+
+ if (strcmp(VarName, VariableName)==0) { // Found it
+ return Equal;
+ }
+ Next:
+ Line++;
+ }
+
+ return NULL;
+}
+
+
+int hex2num(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+
+int hex2byte(const char *hex)
+{
+ int a, b;
+ a = hex2num(*hex++);
+ if (a < 0)
+ return -1;
+ b = hex2num(*hex++);
+ if (b < 0)
+ return -1;
+ return (a << 4) | b;
+}
+
+
+int hexstr2bin(const char *hex, unsigned char *buffer, int len)
+{
+ int i;
+ int a;
+ const char *ipos = hex;
+ unsigned char *opos = buffer;
+
+ for (i = 0; i < len; i++) {
+ a = hex2byte(ipos);
+ if (a < 0)
+ return -1;
+ *opos++ = (unsigned char) a;
+ ipos += 2;
+ }
+ return 0;
+}
+
+
+void close_all()
+{
+ int i;
+ if (Messages) {
+ for ( i = 0; i < MSG_DIM; i++ ) {
+ free(Messages[i]);
+ }
+ free(Messages);
+ }
+ if (active_config)
+ libusb_free_config_descriptor(active_config);
+ if (devh)
+ libusb_close(devh);
+ // libusb_exit will crash on Raspbian 7, crude protection
+#ifndef __ARMEL__
+ if (ctx)
+ libusb_exit(NULL);
+#endif
+ if (sysmode)
+ closelog();
+}
+
+
+void abortExit()
+{
+ fflush(output);
+ fflush(stderr);
+ close_all();
+ exit(1);
+}
+
+
+void printVersion()
+{
+ char* version = VERSION;
+ fprintf(output,"\n * usb_modeswitch: handle USB devices with multiple modes\n"
+ " * Version %s (C) Josua Dietze 2017\n"
+ " * Based on libusb1/libusbx\n\n"
+ " ! PLEASE REPORT NEW CONFIGURATIONS !\n\n", version);
+}
+
+
+void printHelp()
+{
+ fprintf(output,"\nUsage: usb_modeswitch [<params>] [-c filename]\n\n"
+ " -h, --help this help\n"
+ " -e, --version print version information and exit\n"
+ " -j, --find-mbim return config no. with MBIM interface, exit\n\n"
+ " -v, --default-vendor NUM vendor ID of original mode (mandatory)\n"
+ " -p, --default-product NUM product ID of original mode (mandatory)\n"
+ " -V, --target-vendor NUM target mode vendor ID (optional)\n"
+ " -P, --target-product NUM target mode product ID (optional)\n"
+ " -C, --target-class NUM target mode device class (optional)\n"
+ " -b, --bus-num NUM system bus number of device (for hard ID)\n"
+ " -g, --device-num NUM system device number (for hard ID)\n"
+ " -m, --message-endpoint NUM direct the message transfer there (optional)\n"
+ " -M, --message-content <msg> message to send (hex number as string)\n"
+ " -2, --message-content2 <msg> additional messages to send (-n recommended)\n"
+ " -3, --message-content3 <msg> additional messages to send (-n recommended)\n"
+ " -w, --release-delay NUM wait NUM ms before releasing the interface\n"
+ " -n, --need-response obsolete, no effect (always on)\n"
+ " -r, --response-endpoint NUM read response from there (optional)\n"
+ " -K, --std-eject send standard EJECT sequence\n"
+ " -d, --detach-only detach the active driver, no further action\n"
+ " -H, --huawei-mode apply a special procedure\n"
+ " -J, --huawei-new-mode apply a special procedure\n"
+ " -X, --huawei-alt-mode apply a special procedure\n"
+ " -S, --sierra-mode apply a special procedure\n"
+ " -O, --sony-mode apply a special procedure\n"
+ " -G, --gct-mode apply a special procedure\n"
+ " -N, --sequans-mode apply a special procedure\n"
+ " -A, --mobileaction-mode apply a special procedure\n"
+ " -T, --kobil-mode apply a special procedure\n"
+ " -L, --cisco-mode apply a special procedure\n"
+ " -B, --qisda-mode apply a special procedure\n"
+ " -E, --quanta-mode apply a special procedure\n"
+ " -F, --pantech-mode NUM apply a special procedure, pass NUM through\n"
+ " -Z, --blackberry-mode apply a special procedure\n"
+ " -U, --option-mode apply a special procedure\n"
+ " -R, --reset-usb reset the device after all other actions\n"
+ " -Q, --quiet don't show progress or error messages\n"
+ " -W, --verbose print all settings and debug output\n"
+ " -D, --sysmode specific result and syslog message\n"
+ " -s, --check-success <seconds> switching result check with timeout\n"
+ " -I, --inquire obsolete, no effect\n\n"
+ " -c, --config-file <filename> load long configuration from file\n\n"
+ " -t, --stdinput read long configuration from stdin\n\n"
+ " -f, --long-config <text> get long configuration from string\n\n"
+ " -i, --interface NUM select initial USB interface (default 0)\n"
+ " -u, --configuration NUM select USB configuration\n"
+ " -a, --altsetting NUM select alternative USB interface setting\n\n");
+}
diff --git a/usb_modeswitch.conf b/usb_modeswitch.conf
new file mode 100644
index 0000000..e1643b6
--- /dev/null
+++ b/usb_modeswitch.conf
@@ -0,0 +1,40 @@
+# Configuration for the usb_modeswitch package, a mode switching tool for
+# USB devices providing multiple states or modes
+#
+# Evaluated by the wrapper script /usr/sbin/usb_modeswitch_dispatcher
+#
+# To enable an option, set it to "1", "yes" or "true" (case doesn't matter)
+# Everything else counts as "disable"
+
+
+# Disable automatic mode switching globally (e.g. to access the original
+# install storage)
+
+DisableSwitching=0
+
+# Disable check for MBIM module presence and configuration globally (to aid
+# special embedded environments). Available as per-device parameter
+# 'NoMBIMCheck'
+
+DisableMBIMGlobal=0
+
+# Enable logging (results in a extensive report file in /var/log, named
+# "usb_modeswitch_<interface-name>" and probably others
+
+EnableLogging=0
+
+# Optional increase of "delay_use" for the usb-storage driver; there are hints
+# that a recent kernel default change to 1 sec. may lead to problems, particu-
+# larly with USB 3.0 ports. Set this to at least 3 (seconds) in that case.
+# Does nothing if the current system value is same or higher
+
+#SetStorageDelay=4
+
+# If the configuration for your Huawei modem uses the standard switching method,
+# you can globally set an alternative method here which provides a different mode:
+# either plain serial PPP (for older modems) or NCM which integrates nicely
+# with recent Linux distributions, possibly avoiding configuration through a
+# modem built-in web page. All other modems are not affected.
+# Available as per-device parameter 'AltHuaweiMode'
+
+HuaweiAltModeGlobal=0
diff --git a/usb_modeswitch.h b/usb_modeswitch.h
new file mode 100644
index 0000000..018c0e9
--- /dev/null
+++ b/usb_modeswitch.h
@@ -0,0 +1,116 @@
+/*
+ This file is part of usb_modeswitch, a mode switching tool for controlling
+ the mode of 'multi-state' USB devices
+
+ Version 2.5.2, 2017/12/31
+ Copyright (C) 2007 - 2017 Josua Dietze
+
+ Config file parsing stuff borrowed from Guillaume Dargaud
+ (http://www.gdargaud.net/Hack/SourceCode.html)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details:
+
+ http://www.gnu.org/licenses/gpl.txt
+
+*/
+
+#include <stdlib.h>
+#include <libusb.h>
+
+void readConfigFile(const char *configFilename);
+void printConfig();
+int switchSendMessage();
+int switchConfiguration();
+int switchAltSetting();
+void switchHuaweiMode();
+
+void switchSierraMode();
+void switchGCTMode();
+void switchKobilMode();
+void switchQisdaMode();
+void switchQuantaMode();
+void switchSequansMode();
+void switchActionMode();
+void switchBlackberryMode();
+void switchPantechMode();
+void switchCiscoMode();
+int switchSonyMode();
+int detachDrivers();
+int checkSuccess();
+int sendMessage(char* message, int count);
+int write_bulk(int endpoint, unsigned char *message, int length);
+int read_bulk(int endpoint, unsigned char *buffer, int length);
+void release_usb_device(int dummy);
+struct libusb_device* search_devices( int *numFound, int vendor, char* productList,
+ int targetClass, int configuration, int mode);
+int find_first_bulk_endpoint(int direction);
+int get_current_config_value();
+int get_interface_class();
+char* ReadParseParam(const char* FileName, char *VariableName);
+int hex2num(char c);
+int hex2byte(const char *hex);
+int hexstr2bin(const char *hex, unsigned char *buffer, int len);
+void printVersion();
+void printHelp();
+void close_all();
+void abortExit();
+int readArguments(int argc, char **argv);
+void deviceDescription();
+void resetUSB();
+void release_usb_device(int dummy);
+int findMBIMConfig(int vendor, int product, int mode);
+
+
+// Boolean
+#define and &&
+#define or ||
+#define not !
+
+// Bitwise
+#define bitand &
+#define bitor |
+#define compl ~
+#define xor ^
+
+// Equals
+#define and_eq &=
+#define not_eq !=
+#define or_eq |=
+#define xor_eq ^=
+
+extern char* ReadParseParam(const char* FileName, char *VariableName);
+
+extern char *TempPP;
+
+#define ParseParamString(ParamFileName, Str) \
+ if ((TempPP=ReadParseParam((ParamFileName), #Str))!=NULL) \
+ strcpy(Str, TempPP); else Str[0]='\0'
+
+#define ParseParamInt(ParamFileName, Int) \
+ if ((TempPP=ReadParseParam((ParamFileName), #Int))!=NULL) \
+ Int=atoi(TempPP)
+
+#define ParseParamHex(ParamFileName, Int) \
+ if ((TempPP=ReadParseParam((ParamFileName), #Int))!=NULL) \
+ Int=strtol(TempPP, NULL, 16)
+
+#define ParseParamFloat(ParamFileName, Flt) \
+ if ((TempPP=ReadParseParam((ParamFileName), #Flt))!=NULL) \
+ Flt=atof(TempPP)
+
+#define ParseParamBool(ParamFileName, B) \
+ if ((TempPP=ReadParseParam((ParamFileName), #B))!=NULL) \
+ B=(toupper(TempPP[0])=='Y' || toupper(TempPP[0])=='T'|| TempPP[0]=='1'); else B=0
+
+#define ParseParamBoolMap(ParamFileName, B, M, Const) \
+ if ((TempPP=ReadParseParam((ParamFileName), #B))!=NULL) \
+ if (toupper(TempPP[0])=='Y' || toupper(TempPP[0])=='T'|| TempPP[0]=='1') \
+ M=M+Const
diff --git a/usb_modeswitch.sh b/usb_modeswitch.sh
new file mode 100755
index 0000000..eb3fa3e
--- /dev/null
+++ b/usb_modeswitch.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+# part of usb_modeswitch 2.5.2
+device_in()
+{
+ if [ ! -e /var/lib/usb_modeswitch/$1 ]; then
+ return 0
+ fi
+ while read line
+ do
+ if [ $(expr "$line" : "$2:$3") != 0 ]; then
+ return 1
+ fi
+ done </var/lib/usb_modeswitch/$1
+ if [ $(expr "$line" : "$2:$3") != 0 ]; then
+ return 1
+ fi
+ return 0
+}
+
+if [ $(expr "$1" : "--.*") ]; then
+ p_id=$4
+ if [ -z $p_id ]; then
+ prod=$5
+ if [ -z $prod ]; then
+ prod=$3
+ fi
+ prod=${prod%/*}
+ v_id=0x${prod%/*}
+ p_id=0x${prod#*/}
+ if [ "$v_id" = "0x" ]; then
+ v_id="0"
+ p_id="0"
+ fi
+ v_id="$(printf %04x $(($v_id)))"
+ p_id="$(printf %04x $(($p_id)))"
+ else
+ v_id=$3
+ fi
+fi
+PATH=/sbin:/usr/sbin:$PATH
+case "$1" in
+ --driver-bind)
+ # driver binding code removed
+ exit 0
+ ;;
+ --symlink-name)
+ device_in "link_list" $v_id $p_id
+ if [ "$?" = "1" ]; then
+ if [ -e "/usr/sbin/usb_modeswitch_dispatcher" ]; then
+ exec usb_modeswitch_dispatcher $1 $2 2>>/dev/null
+ fi
+ fi
+ exit 0
+ ;;
+esac
+
+IFS='/' read -r p1 p2 <<EOF
+$1
+EOF
+if [ "$p2" = "" -a "$p1" != "" ]; then
+ p2=$p1
+fi
+
+PATH=/bin:/sbin:/usr/bin:/usr/sbin
+init_path=`readlink -f /sbin/init`
+if [ `basename $init_path` = "systemd" ]; then
+ systemctl --no-block start usb_modeswitch@$p2.service
+elif [ -e "/etc/init/usb-modeswitch-upstart.conf" ]; then
+ initctl emit --no-wait usb-modeswitch-upstart UMS_PARAM=$p2
+else
+ # only old distros, new udev will kill all subprocesses
+ exec 1<&- 2<&- 5<&- 7<&-
+ exec usb_modeswitch_dispatcher --switch-mode $p2 &
+fi
+exit 0
diff --git a/usb_modeswitch.tcl b/usb_modeswitch.tcl
new file mode 100755
index 0000000..d2ee50c
--- /dev/null
+++ b/usb_modeswitch.tcl
@@ -0,0 +1,1005 @@
+#!/usr/bin/tclsh
+
+# Wrapper (tcl) for usb_modeswitch, called from
+# /lib/udev/rules.d/40-usb_modeswitch.rules
+# (part of data pack "usb-modeswitch-data") via
+# /lib/udev/usb_modeswitch
+#
+# Does ID check on newly discovered USB devices and calls
+# the mode switching program with the matching parameter
+# file from /usr/share/usb_modeswitch
+#
+# Part of usb-modeswitch-2.5.2 package
+# (C) Josua Dietze 2009-2017
+
+set arg0 [lindex $argv 0]
+if [regexp {\.tcl$} $arg0] {
+ if [file exists $arg0] {
+ set argv [lrange $argv 1 end]
+ source $arg0
+ exit
+ }
+}
+
+# Setting of these switches is done in the global config
+# file (/etc/usb_modeswitch.conf) if available
+
+set flags(logging) 1
+set flags(noswitching) 0
+set flags(stordelay) 0
+set flags(nombim) 0
+
+set flags(logwrite) 0
+# also settable in device config files
+set flags(nombim) 0
+set flags(althuawei) 0
+
+# Execution starts at file bottom
+
+proc {Main} {argv argc} {
+
+global scsi usb config match device flags setup devdir loginit
+
+set flags(config) ""
+Log "[ParseGlobalConfig]"
+
+if {$flags(stordelay) > 0} {
+ SetStorageDelay $flags(stordelay)
+}
+
+
+# The facility to add a symbolic link pointing to the
+# ttyUSB port which provides interrupt transfer, i.e.
+# the port to connect through.
+# Will check for interrupt endpoint in ttyUSB port (lowest if
+# there is more than one); if found, return "gsmmodem[n]" name
+# to udev for symlink creation
+
+# This is run once for every port of LISTED devices by
+# a udev rule
+
+if {[lindex $argv 0] == "--symlink-name"} {
+ puts -nonewline [SymLinkName [lindex $argv 1]]
+ SafeExit
+}
+
+# arg0: the bus id for the device (udev: %b), now deprecated
+# arg1: the "kernel name" for the device (udev: %k)
+#
+# From version 2.5.0 upward %b is removed by udev sh script
+# which can handle old and new udev params ('%b/%k' and '%k')
+
+Log "Raw parameters: $argv"
+set device "noname"
+if {[lindex $argv 0] == "--switch-mode"} {
+ if [string length [lindex $argv 1]] {
+ set arg1 [lindex $argv 1]
+ } else {
+ Log "\nNo data from udev. Exit"
+ SafeExit
+ }
+} else {
+ Log "\nNo command given. Exit"
+ SafeExit
+}
+
+if {![regexp {(.*?):.*$} $arg1 d device]} {
+ if {![regexp {([0-9]+-[0-9]+\.?[0-9]*.*)} $arg1 d device]} {
+ Log "Could not determine device dir from udev values! Exit"
+ SafeExit
+ }
+}
+set flags(logwrite) 1
+
+set setup(dbdir) /usr/share/usb_modeswitch
+set setup(dbdir_etc) /etc/usb_modeswitch.d
+if {![file exists $setup(dbdir)] && ![file exists $setup(dbdir_etc)]} {
+ Log "\nError: no config database found in /usr/share or /etc. Exit"
+ SafeExit
+}
+
+set bindir /usr/sbin
+set devList1 {}
+set devList2 {}
+set ifChk 0
+
+set devdir /sys/bus/usb/devices/$device
+if {![file isdirectory $devdir]} {
+ Log "Top device directory not found ($devdir)! Exit"
+ SafeExit
+}
+Log "Use top device dir $devdir"
+
+set iface 0
+Log "Check class of first interface ..."
+set config(class) [IfClass 0 $devdir]
+if {$config(class) < 0} {
+ Log " No access to interface 0. Exit"
+ SafeExit
+}
+Log " Interface 0 class is $config(class)."
+
+set ifdir [file tail [IfDir $iface $devdir]]
+regexp {:([0-9]+\.[0-9]+)$} $ifdir d iface
+
+# Mapping of the short string identifiers (in the config
+# file names) to the long name used here
+#
+# If we need them it's a snap to add new attributes here!
+
+set match(sVe) scsi(vendor)
+set match(sMo) scsi(model)
+set match(sRe) scsi(rev)
+set match(uMa) usb(manufacturer)
+set match(uPr) usb(product)
+set match(uSe) usb(serial)
+
+
+# Now reading the USB attributes
+if {![ReadUSBAttrs $devdir]} {
+ Log "USB attributes not found in sysfs tree. Exit"
+ SafeExit
+}
+set config(vendor) $usb(idVendor)
+set config(product) $usb(idProduct)
+
+
+if $flags(logging) {
+ Log "\n----------------\nUSB values from sysfs:"
+ foreach attr {manufacturer product serial} {
+ Log " $attr\t$usb($attr)"
+ }
+ Log "----------------"
+}
+
+if $flags(noswitching) {
+ SysLog "usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)"
+ Log "\nSwitching globally disabled. Exit"
+ SafeExit
+}
+
+if {$usb(bNumConfigurations) == "1"} {
+ set configParam "-u -1"
+ Log "bNumConfigurations is 1 - don't check for active configuration"
+} else {
+ set configParam ""
+}
+
+# Check (and switch) for operating system if Huawei device present
+
+set flags(os) "linux"
+if {$usb(idVendor) == "12d1" && [regexp -nocase {android} [exec cat /proc/version]]} {
+ set flags(os) "android"
+}
+if {$flags(os) == "android"} {
+ set configList [ConfigGet conflist $usb(idVendor):#android]
+} else {
+ set configList [ConfigGet conflist $usb(idVendor):$usb(idProduct)]
+}
+
+if {[llength $configList] == 0} {
+ Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exit"
+ SafeExit
+}
+Log "ConfigList: $configList"
+
+# Check if there is more than one config file for this USB ID,
+# which would make an attribute test necessary. If so, check if
+# SCSI values are needed
+
+set scsiNeeded 0
+if {[llength $configList] > 1} {
+ if [regexp {:s} $configList] {
+ set scsiNeeded 1
+ }
+}
+if $scsiNeeded {
+ if [ReadSCSIAttrs $devdir:$iface] {
+ Log "----------------\nSCSI values from sysfs:"
+ foreach attr {vendor model rev} {
+ Log " $attr\t$scsi($attr)"
+ }
+ Log "----------------"
+ } else {
+ Log "Could not get SCSI attributes, exclude devices with SCSI match"
+ }
+} else {
+ Log "SCSI attributes not needed, move on"
+}
+
+# Now check for a matching config file. Matching is done
+# by MatchDevice
+
+set report ""
+foreach mconfig $configList {
+
+ # skipping installer leftovers like "*.rpmnew"
+ if [regexp {\.(dpkg|rpm)} $mconfig] {continue}
+
+ Log "Check config: $mconfig"
+ if [MatchDevice $mconfig] {
+ Log "! matched. Read config data"
+ set flags(config) [ConfigGet conffile $mconfig]
+ break
+ } else {
+ Log "* no match, don't use this config"
+ }
+}
+if {$flags(config) == ""} {
+ Log "No matching config file found. Exit"
+ SafeExit
+}
+
+ParseDeviceConfig $flags(config)
+
+if [regexp -nocase {0x([0-9a-f]+)} $config(TargetClass) d tc] {
+ if {$tc == $config(class)} {
+ Log "Class of interface 0 matches target. Do nothing"
+ set report "ok:busdev"
+ }
+}
+
+if [string length $usb(busnum)] {
+ set busParam "-b [string trimleft $usb(busnum) 0]"
+ set devParam "-g [string trimleft $usb(devnum) 0]"
+} else {
+ set busParam ""
+ set devParam ""
+}
+if [regexp -nocase $flags(os) $flags(config)] {
+ Log "Note: Using generic manufacturer configuration for \"$flags(os)\""
+}
+if $flags(althuawei) {
+ regsub {HuaweiNewMode} $flags(config) {HuaweiAltMode} flags(config)
+ Log "Alternative Huawei mode set globally, modify config"
+}
+if $flags(nombim) {
+ set config(NoMBIMCheck) 1
+}
+if {$config(NoMBIMCheck)==0 && $usb(bNumConfigurations) > 1} {
+ Log "Device may have an MBIM configuration, check driver ..."
+ if [CheckMBIM] {
+ Log " driver for MBIM devices is available"
+ Log "Find MBIM configuration number ..."
+ if [catch {set cfgno [exec /usr/sbin/usb_modeswitch -j -Q $busParam $devParam -v $usb(idVendor) -p $usb(idProduct)]} err] {
+ Log "Error when trying to find MBIM configuration, switch to legacy modem mode"
+ } else {
+ set cfgno [string trim $cfgno]
+ if {$cfgno > 0} {
+ set config(Configuration) $cfgno
+ set flags(config) "Configuration=$cfgno"
+ } else {
+ Log " No MBIM configuration found, switch to legacy modem mode"
+ }
+ }
+ } else {
+ Log " no MBIM driver found, switch to legacy modem mode"
+ }
+}
+if [PantechAutoSwitch] {
+ Log "Waiting for Pantech auto-modeswitch"
+ set report "ok:busdev"
+}
+if {$config(Configuration) == 0} {
+ Log "Config file contains dummy method, do nothing. Exit"
+ SafeExit
+}
+# General wait - some devices need this
+after 500
+
+if {$config(WaitBefore) != ""} {
+ Log "Delay time of $config(WaitBefore) seconds"
+ append config(WaitBefore) "000"
+ after $config(WaitBefore)
+}
+
+if {$report == ""} {
+ # Now we are actually switching
+ if $flags(logging) {
+ Log "Command line:\nusb_modeswitch -W -D $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f \$flags(config)"
+ catch {set report [exec /usr/sbin/usb_modeswitch -W -D $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$flags(config)" 2>@1]} report
+ Log "\nVerbose debug output of usb_modeswitch and libusb follows"
+ Log "(Note that some USB errors are to be expected in the process)"
+ Log "--------------------------------"
+ Log $report
+ Log "--------------------------------"
+ Log "(end of usb_modeswitch output)\n"
+ } else {
+ catch {set report [exec /usr/sbin/usb_modeswitch -Q -D $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$flags(config)" 2>@1]} report
+ }
+}
+
+# Switching is complete; success checking was either
+# done by usb_modeswitch and logged via syslog OR bus/dev
+# parameter were used; then we do check for success HERE
+
+if {$config(Configuration) != ""} {
+ set ifdir [regsub {(\d):\d+\.0} $ifdir "\\1:$config(Configuration).0"]
+}
+
+if [regexp {ok:busdev} $report] {
+ if [CheckSuccess $devdir] {
+ Log "Mode switching was successful, found $usb(idVendor):$usb(idProduct) ($usb(manufacturer): $usb(product))"
+ SysLog "usb_modeswitch: switched to $usb(idVendor):$usb(idProduct) on [format %03d $usb(busnum)]/[format %03d $usb(devnum)]"
+ } else {
+ Log "\nTarget config not matching - current values are"
+ LogAttributes
+ Log "\nMode switching may have failed. Exit"
+ SafeExit
+ }
+} else {
+ if {![file isdirectory $devdir]} {
+ Log "Device directory in sysfs is gone! Something went wrong, abort"
+ SafeExit
+ }
+ if {![regexp {ok:} $report]} {
+ Log "\nCore program reported switching failure. Exit"
+ SafeExit
+ }
+ # Give the device another second if it's not fully back yet
+ if {![file exists $devdir/idProduct]} {
+ after 1000
+ }
+ ReadUSBAttrs $devdir $ifdir
+}
+
+# driver binding removed !!
+
+if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
+ if {![regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct)]} {
+ Log "No target vendor/product ID found or given, can't continue. Abort"
+ SafeExit
+ }
+}
+# wait for drivers to bind
+after 500
+if {[llength [glob -nocomplain $devdir/$ifdir/ttyUSB*]] > 0} {
+ Log "Serial USB driver bound to interface 0\n will try to guess and symlink modem port on next connect"
+ AddToList link_list $usb(idVendor):$usb(idProduct)
+}
+
+# In newer kernels there is a switch to avoid the use of a device
+# reset (e.g. from usb-storage) which would possibly switch back
+# a mode-switching device to initial mode
+if [regexp {ok:} $report] {
+ Log "Check for AVOID_RESET_QUIRK kernel attribute"
+ if [file exists $devdir/avoid_reset_quirk] {
+ if [catch {exec echo "1" >$devdir/avoid_reset_quirk 2>/dev/null} err] {
+ Log " Error setting the attribute: $err"
+ } else {
+ Log " AVOID_RESET_QUIRK activated"
+ }
+ } else {
+ Log " not present in this kernel"
+ }
+}
+
+Log "\nAll done, exit\n"
+SafeExit
+
+}
+# end of proc {Main}
+
+
+proc {ReadSCSIAttrs} {topdir} {
+
+global scsi
+set counter 0
+set sysdir $topdir
+Log "Check storage tree in sysfs ..."
+while {$counter < 20} {
+ Log " loop $counter/20"
+ if {![file isdirectory $sysdir]} {
+ # Device is gone. Unplugged? Switched by kernel?
+ Log " sysfs device tree is gone; abort SCSI value check"
+ return 0
+ }
+ # Searching the storage/SCSI tree; might take a while
+ if {[set dirList [glob -nocomplain $topdir/host*]] != ""} {
+ set sysdir [lindex $dirList 0]
+ if {[set dirList [glob -nocomplain $sysdir/target*]] != ""} {
+ set sysdir [lindex $dirList 0]
+ regexp {.*target(.*)} $sysdir d subdir
+ if {[set dirList [glob -nocomplain $sysdir/$subdir*]] != ""} {
+ set sysdir [lindex $dirList 0]
+ if [file exists $sysdir/vendor] {
+ Log " Storage tree is ready"
+ break
+ }
+ }
+ }
+ }
+ after 500
+ incr counter
+}
+if {$counter == 20} {
+ Log "SCSI tree not found; you may want to check if this path/file exists:"
+ Log "$sysdir/vendor\n"
+ return 0
+}
+
+Log "Read SCSI values ..."
+foreach attr {vendor model rev} {
+ if [file exists $sysdir/$attr] {
+ set rc [open $sysdir/$attr r]
+ set scsi($attr) [read -nonewline $rc]
+ close $rc
+ } else {
+ set scsi($attr) ""
+ Log "Warning: SCSI attribute \"$attr\" not found."
+ }
+}
+return 1
+
+}
+# end of proc {ReadSCSIAttrs}
+
+
+proc {ReadUSBAttrs} {dir args} {
+
+global usb
+
+set attrList {idVendor idProduct bConfigurationValue manufacturer product serial devnum busnum bNumConfigurations}
+set mandatoryList {idVendor idProduct bNumConfigurations}
+set result 1
+if {$args != ""} {
+ lappend attrList "$args/bInterfaceClass"
+ lappend mandatoryList "$args/bInterfaceClass"
+}
+foreach attr $attrList {
+ if [file exists $dir/$attr] {
+ set rc [open $dir/$attr r]
+ set usb($attr) [string trim [read -nonewline $rc]]
+ close $rc
+ } else {
+ set usb($attr) ""
+ if {[lsearch $mandatoryList $attr] > -1} {
+ set result 0
+ }
+ if {$attr == "serial"} {continue}
+ Log " Warning: USB attribute \"$attr\" not found"
+ }
+}
+return $result
+
+}
+# end of proc {ReadUSBAttrs}
+
+
+proc {MatchDevice} {config} {
+
+global scsi usb match
+
+set devinfo [file tail $config]
+set infoList [split $devinfo :]
+set stringList [lrange $infoList 2 end]
+if {[llength $stringList] == 0} {return 1}
+
+foreach teststring $stringList {
+ if {$teststring == "?"} {return 0}
+ set tokenList [split $teststring =]
+ set id [lindex $tokenList 0]
+ set matchstring [lindex $tokenList 1]
+ set blankstring ""
+ regsub -all {_} $matchstring { } blankstring
+ Log "match $match($id)"
+ Log " string1 (exact): $matchstring"
+ Log " string2 (blanks): $blankstring"
+ Log " device string: [set $match($id)]"
+ if {!([string match *$matchstring* [set $match($id)]] || [string match *$blankstring* [set $match($id)]])} {
+ return 0
+ }
+}
+return 1
+
+}
+# end of proc {MatchDevice}
+
+
+proc {ParseGlobalConfig} {} {
+
+global flags
+set configFile ""
+set places [list /etc/usb_modeswitch.conf /etc/sysconfig/usb_modeswitch /etc/default/usb_modeswitch]
+foreach cfg $places {
+ if [file exists $cfg] {
+ set configFile $cfg
+ break
+ }
+}
+if {$configFile == ""} {return}
+
+set rc [open $configFile r]
+while {![eof $rc]} {
+ gets $rc line
+ if [regexp {^#} [string trim $line]] {continue}
+ if [regexp {DisableMBIMGlobal\s*=\s*([^\s]+)} $line d val] {
+ if [regexp -nocase {1|yes|true} $val] {
+ set flags(nombim) 1
+ }
+ }
+ if [regexp {DisableSwitching\s*=\s*([^\s]+)} $line d val] {
+ if [regexp -nocase {1|yes|true} $val] {
+ set flags(noswitching) 1
+ }
+ }
+ if [regexp {EnableLogging\s*=\s*([^\s]+)} $line d val] {
+ if [regexp -nocase {0|no|false} $val] {
+ set flags(logging) 0
+ }
+ }
+ if [regexp {SetStorageDelay\s*=\s*([^\s]+)} $line d val] {
+ if [regexp {\d+} $val] {
+ set flags(stordelay) $val
+ }
+ }
+ if [regexp {HuaweiAltModeGlobal\s*=\s*([^\s]+)} $line d val] {
+ if [regexp -nocase {1|yes|true} $val] {
+ set flags(althuawei) 1
+ }
+ }
+
+}
+return "Use global config file: $configFile"
+
+}
+# end of proc {ParseGlobalConfig}
+
+
+proc ParseDeviceConfig {cfg} {
+
+global config
+set config(WaitBefore) ""
+set config(TargetVendor) ""
+set config(TargetProduct) ""
+set config(TargetClass) ""
+set config(Configuration) ""
+set config(NoMBIMCheck) 0
+set config(PantechMode) 0
+set config(CheckSuccess) 20
+
+foreach pname [lsort [array names config]] {
+ if [regexp -line "^\[^# \]*?$pname.*?= *(0x(\\w+)|\"(\[0-9a-fA-F,\]+)\"|(\[0-9\]+)) *\$" $cfg d config($pname)] {
+# Log "config: $pname set to $config($pname)"
+ }
+}
+
+set config(WaitBefore) [string trimleft $config(WaitBefore) 0]
+
+}
+# end of proc {ParseDeviceConfig}
+
+
+proc ConfigGet {command config} {
+
+global setup usb flags
+
+switch $command {
+
+ conflist {
+ # Unpackaged configs first; sorting is essential for priority
+ set configList [lsort -decreasing [glob -nocomplain $setup(dbdir_etc)/$config*]]
+ set configList [concat $configList [lsort -decreasing [glob -nocomplain $setup(dbdir)/$config*]]]
+ eval lappend configList [glob -nocomplain $setup(dbdir)/$usb(idVendor):#$flags(os)]
+ if [file exists $setup(dbdir)/configPack.tar.gz] {
+ Log "Found packed config collection $setup(dbdir)/configPack.tar.gz"
+ if [catch {set packedList [exec tar -tzf $setup(dbdir)/configPack.tar.gz 2>/dev/null]} err] {
+ Log "Error: problem opening config package; tar returned\n $err"
+ return {}
+ }
+ set packedList [split $packedList \n]
+ set packedConfigList [lsort -decreasing [lsearch -glob -all -inline $packedList $config*]]
+ lappend packedConfigList [lsearch -inline $packedList $usb(idVendor):#$flags(os)]
+ # Now add packaged configs with a mark, again sorted for priority
+ foreach packedConfig $packedConfigList {
+ lappend configList "pack/$packedConfig"
+ }
+ }
+ return $configList
+ }
+ conffile {
+ if [regexp {^pack/} $config] {
+ set config [regsub {pack/} $config {}]
+ Log "Extract config $config from collection $setup(dbdir)/configPack.tar.gz"
+ set configContent [exec tar -xzOf $setup(dbdir)/configPack.tar.gz $config 2>/dev/null]
+ } else {
+ if [regexp [list $setup(dbdir_etc)] $config] {
+ Log "Use config file from override folder $setup(dbdir_etc)"
+ SysLog "usb_modeswitch: use overriding config file $config; make sure this is intended"
+ SysLog "usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files"
+ }
+ set rc [open $config r]
+ set configContent [read $rc]
+ close $rc
+ }
+ return $configContent
+ }
+}
+
+}
+# end of proc {ConfigGet}
+
+proc {Log} {msg} {
+
+global flags device loginit
+
+if {$flags(logging) == 0} {return}
+
+if $flags(logwrite) {
+ if [string length $loginit] {
+ exec echo "\nUSB_ModeSwitch log from [clock format [clock seconds]]" >/var/log/usb_modeswitch_$device
+ exec echo "$loginit" >>/var/log/usb_modeswitch_$device
+ set loginit ""
+ }
+ exec echo $msg >>/var/log/usb_modeswitch_$device
+} else {
+ append loginit "\n$msg"
+}
+
+}
+# end of proc {Log}
+
+
+# Writing the log file and exit
+proc {SafeExit} {} {
+
+global flags
+set flags(logwrite) 1
+Log ""
+exit
+
+}
+# end of proc {SafeExit}
+
+
+proc {SymLinkName} {path} {
+global device flags
+
+proc {hasInterrupt} {ifDir} {
+ if {[llength [glob -nocomplain $ifDir/ttyUSB*]] == 0} {
+ Log " no ttyUSB interface - skip endpoint check"
+ return 0
+ }
+ foreach epDir [glob -nocomplain $ifDir/ep_*] {
+ set e [file tail $epDir]
+ Log " check $e ..."
+ if [file exists $epDir/type] {
+ set rc [open $epDir/type r]
+ set type [read $rc]
+ close $rc
+ if [regexp {Interrupt} $type] {
+ Log " $e has interrupt transfer type"
+ return 1
+ }
+ }
+ }
+ return 0
+}
+
+set loginit "usb_modeswitch called with --symlink-name\n parameter: $path\n"
+
+# In case the device path is returned as /class/tty/ttyUSB,
+# get the USB device path from linked tree "device"
+set linkpath /sys$path/device
+if [file exists $linkpath] {
+ if {[file type $linkpath] == "link"} {
+ set rawpath [file readlink $linkpath]
+ set trimpath [regsub -all {\.\./} $rawpath {}]
+ if [file isdirectory /sys/$trimpath] {
+ append loginit "\n Use path $path\n"
+ set path /$trimpath
+ }
+ }
+}
+if {![regexp {([0-9]+-[0-9]+[\.0-9]*:[^/]*).*(ttyUSB[0-9]+)} $path d myDev myPort]} {
+ if $flags(logging) {
+ set device [clock clicks]
+ set flags(logwrite) 1
+ Log "$loginit\nThis is not a ttyUSB port. Abort"
+ }
+ return ""
+}
+
+set device ttyUSB_$myDev
+set flags(logwrite) 1
+Log "$loginit\nMy name is $myPort\n"
+
+if {![regexp {(.*?[0-9]+)\.([0-9]+)/ttyUSB} /sys$path d ifRoot ifNum]} {
+ Log "Could not find interface in path\n $path. Abort"
+ return ""
+}
+
+set ifDir $ifRoot.$ifNum
+
+Log "Check my endpoints ...\n in $ifDir"
+if [hasInterrupt $ifDir] {
+ Log "\n--> I am an interrupt port"
+ set rightPort 1
+} else {
+ Log "\n--> I am not an interrupt port\n"
+ set rightPort 0
+}
+
+# There are devices with more than one interrupt interface.
+# Assume that the lowest of these is usable. Check all
+# possible lower interfaces
+
+if { $rightPort && ($ifNum > 0) } {
+ Log "\nLook for lower ports with interrupt endpoints"
+ for {set i 0} {$i < $ifNum} {incr i} {
+ set ifDir $ifRoot.$i
+ Log " in ifDir $ifDir ..."
+ if [hasInterrupt $ifDir] {
+ Log "\n--> found an interrupt interface below me\n"
+ set rightPort 0
+ break
+ }
+ }
+}
+if {$rightPort == 0} {
+ Log "Return empty name and exit"
+ return ""
+}
+
+Log "\n--> No interrupt interface below me\n"
+
+cd /dev
+set idx 2
+set symlinkName "gsmmodem"
+while {$idx < 256} {
+ if {![file exists $symlinkName]} {
+ set placeholder [open /dev/$symlinkName w]
+ close $placeholder
+ break
+ }
+ set symlinkName gsmmodem$idx
+ incr idx
+}
+if {$idx == 256} {return ""}
+
+Log "Return symlink name \"$symlinkName\" and exit"
+return $symlinkName
+
+}
+# end of proc {SymLinkName}
+
+
+# Add USB ID to list of devices needing later treatment
+proc {AddToList} {name id} {
+
+set listfile /var/lib/usb_modeswitch/$name
+if [file exists $listfile] {
+ set rc [open $listfile r]
+ set buffer [read $rc]
+ close $rc
+ if [string match *$id* $buffer] {
+ return
+ }
+ set idList [split [string trim $buffer] \n]
+}
+lappend idList $id
+set buffer [join $idList "\n"]
+if [catch {set lc [open $listfile w]}] {return}
+puts $lc $buffer
+close $lc
+
+}
+# end of proc {AddToList}
+
+
+proc {CheckSuccess} {devdir} {
+
+global config usb flags
+
+# For Cisco AM10, target device not on same port
+if {$usb(idVendor) == "1307" && $usb(idProduct) == "1169"} {
+ set devdir [string range $devdir 0 end-1]2
+}
+set ifdir [file tail [IfDir 0 $devdir]]
+if {[string length $config(TargetClass)] || [string length $config(Configuration)]} {
+ set config(TargetVendor) $usb(idVendor)
+ set config(TargetProduct) $usb(idProduct)
+}
+Log "Check success of mode switch for max. $config(CheckSuccess) seconds ..."
+
+set expected 1
+for {set i 1} {$i <= $config(CheckSuccess)} {incr i} {
+ after 1000
+ if {![file isdirectory $devdir]} {
+ Log " Wait for device file system ($i sec.) ..."
+ continue
+ } else {
+ Log " Read attributes ..."
+ }
+ set ifdir [IfDir 0 $devdir]
+ if {$ifdir == ""} {continue}
+ set ifdir [file tail $ifdir]
+ if {![ReadUSBAttrs $devdir $ifdir]} {
+ Log " Essential attributes are missing, continue wait ..."
+ continue
+ }
+ if [string length $config(Configuration)] {
+ if {$usb(bConfigurationValue) != $config(Configuration)} {continue}
+ }
+ if [string length $config(TargetClass)] {
+ if {![regexp -nocase $usb($ifdir/bInterfaceClass) $config(TargetClass)]} {
+ if {$config(class) != $usb($ifdir/bInterfaceClass} {
+ set expected 0
+ } else {continue}
+ }
+ }
+ if {![regexp -nocase $usb(idVendor) $config(TargetVendor)]} {
+ if {![regexp -nocase $usb(idVendor) $config(vendor)]} {
+ set expected 0
+ } else {continue}
+ }
+ if {![regexp -nocase $usb(idProduct) $config(TargetProduct)]} {
+ if {![regexp -nocase $usb(idProduct) $config(product)]} {
+ set expected 0
+ } else {continue}
+ }
+ # Arriving here means that device attributes have changed
+ if $expected {
+ Log " All attributes matched"
+ } else {
+ if [regexp -nocase {/[0-9a-f]+:#} $flags(config)] {
+ Log " idProduct has changed after generic mode-switch, assume success"
+ } else {
+ if [regexp {HuaweiAltMode} $flags(config)] {
+ Log " Alternative target attributes found, assume success"
+ } else {
+ Log " Attributes are different but target values are unexpected:"
+ LogAttributes
+ }
+ }
+ }
+ break
+}
+if {$i > 20} {return 0} else {return 1}
+
+}
+# end of proc {CheckSuccess}
+
+
+proc {IfDir} {iface devdir} {
+
+set allfiles [glob -nocomplain $devdir/*]
+set files [glob -nocomplain $devdir/*.$iface]
+if {[llength $files] == 0} {
+ return ""
+}
+set ifdir [lindex $files 0]
+if {![file isdirectory $ifdir]} {
+ return ""
+}
+return $ifdir
+
+}
+# end of proc {IfDir}
+
+proc {IfClass} {iface devdir} {
+
+set ifdir [IfDir $iface $devdir]
+
+if {![file exists $ifdir/bInterfaceClass]} {
+ return -1
+}
+set rc [open $ifdir/bInterfaceClass r]
+set c [read $rc]
+close $rc
+return [string trim $c]
+
+}
+# end of proc {IfClass}
+
+
+proc {SysLog} {msg} {
+
+global flags
+if {![info exists flags(logger)]} {
+ set flags(logger) ""
+ foreach fn {/bin/logger /usr/bin/logger} {
+ if [file exists $fn] {
+ set flags(logger) $fn
+ }
+ }
+ Log "Logger is $flags(logger)"
+}
+if {$flags(logger) == ""} {
+ Log "Can't add system message, no syslog helper found"
+ return
+}
+catch {exec $flags(logger) -p syslog.notice "$msg" 2>/dev/null}
+
+}
+# end of proc {SysLog}
+
+proc {SetStorageDelay} {secs} {
+
+Log "Adjust delay for USB storage devices ..."
+set attrib /sys/module/usb_storage/parameters/delay_use
+if {![file exists $attrib]} {
+ Log "Error: could not find delay_use attribute"
+ return
+}
+if [catch {set ch [open $attrib r+]} err] {
+ Log "Error: could not access delay_use attribute: $err"
+ return
+}
+if {[read $ch] < $secs} {
+ seek $ch 0 start
+ puts -nonewline $ch $secs
+ Log " Delay set to $secs seconds\n"
+} else {
+ Log " Current value is higher than $secs. Leave it alone\n"
+}
+close $ch
+
+}
+# end of proc {SetStorageDelay}
+
+proc {CheckMBIM} {} {
+
+set kversion [exec uname -r]
+if [llength [glob -nocomplain /lib/modules/$kversion/kernel/drivers/net/usb/cdc_mbim*]] {return 1}
+if [file exists /sys/bus/usb/drivers/cdc_mbim] {return 1}
+return 0
+
+}
+
+proc {CheckQMI} {} {
+
+set kversion [exec uname -r]
+if [llength [glob -nocomplain /lib/modules/$kversion/kernel/drivers/net/usb/qmi_wwan*]] {return 1}
+if [file exists /sys/bus/usb/drivers/cdc_mbim] {return 1}
+return 0
+
+}
+
+proc {PantechAutoSwitch} {} {
+
+global config flags
+if {$config(PantechMode) == 3} {return 1}
+if {$config(PantechMode) == 1} {
+ if {"$config(vendor):$config(product)" == "10a9:6080"} {
+ set flags(config) [regsub {PantechMode *= *1} $flags(config) "PantechMode=2"]
+ Log " PantechMode changed to 2"
+ return 0
+ } elseif [CheckQMI] {
+ set flags(config) [regsub {PantechMode *= *1} $flags(config) "PantechMode=4"]
+ Log " PantechMode changed to 4"
+ return 0
+ } else {
+ return 1
+ }
+} else {return 0}
+
+}
+
+proc {LogAttributes} {} {
+
+global flags usb
+if $flags(logging) {
+ set attrList {idVendor idProduct bConfigurationValue manufacturer product serial}
+ foreach attr [lsort [array names usb]] {
+ Log " [format %-26s $attr:] $usb($attr)"
+ }
+}
+
+}
+
+proc {HasFF} {devdir} {
+
+set i 0
+while {[set dir [IfDir $i $devdir]] != ""} {
+ set c [exec cat $dir/bInterfaceClass]
+ if {$c == "ff"} {return 1}
+ incr i
+}
+return 0
+
+}
+
+
+# The actual entry point
+Main $argv $argc
diff --git a/usb_modeswitch@.service b/usb_modeswitch@.service
new file mode 100644
index 0000000..f74a8bf
--- /dev/null
+++ b/usb_modeswitch@.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=USB_ModeSwitch_%i
+
+[Service]
+Type=oneshot
+ExecStart=/usr/sbin/usb_modeswitch_dispatcher --switch-mode %i
+#ExecStart=/bin/echo %i
+
diff --git a/usb_modeswitch_dispatcher.1 b/usb_modeswitch_dispatcher.1
new file mode 100644
index 0000000..5b826d4
--- /dev/null
+++ b/usb_modeswitch_dispatcher.1
@@ -0,0 +1,21 @@
+.TH "USB_MODESWITCH_DISPATCHER" "1"
+.SH "NAME"
+usb_modeswitch_dispatcher - Linux wrapper for usb_modeswitch (not intended for direct invocation)
+.SH "SYNOPSIS"
+.PP
+\fBusb_modeswitch_dispatcher\fR
+.SH "DESCRIPTION"
+.PP
+usb_modeswitch_dispatcher will do detailed device checking and will subsequently
+use the Linux-independent usb_modeswitch binary together with the selected device
+config file to switch the mode of certain USB devices.
+.PP
+If no drivers are taking care of the device after the mode switch, the dispatcher
+will try to load and bind the "option" serial driver to any USB interfaces with
+class 0xff, in order to make the device useable in case it is not recognized by that
+driver yet. This may or may not work.
+.PP
+This program is called by udev and is not supposed to be called directly
+by the user.
+.SH SEE ALSO
+.BR usb_modeswitch(1).