debian/0000775000000000000000000000000012320604540007164 5ustar debian/usb-modeswitch.install0000664000000000000000000000031412317322502013510 0ustar usr/sbin/usb_modeswitch usr/sbin/usb_modeswitch_dispatcher lib/udev/usb_modeswitch usr/share/man/* etc/usb_modeswitch.conf usb_modeswitch@.service lib/systemd/system/ usb-modeswitch-upstart.conf etc/init debian/compat0000664000000000000000000000000212317322435010370 0ustar 9 debian/usb-modeswitch.postinst0000664000000000000000000000055112317322435013735 0ustar #!/bin/sh set -e if [ "$1" = configure ]; then # Move "runtime" files to their new and correct location if test -e /etc/usb_modeswitch.d/link_list; then mv -f /etc/usb_modeswitch.d/link_list /var/lib/usb_modeswitch/ fi if test -e /etc/usb_modeswitch.d/bind_list; then mv -f /etc/usb_modeswitch.d/bind_list /var/lib/usb_modeswitch/ fi fi #DEBHELPER# debian/usb-modeswitch.dirs0000664000000000000000000000003012317322435013003 0ustar /var/lib/usb_modeswitch debian/README.Debian0000664000000000000000000000136012317322435011233 0ustar usb-modeswitch for Debian ------------------------- In Debian, usb-modeswitch is supposed to work automagically for all devices known to work : /lib/udev/rules.d/40-usb_modeswitch.rules is a udev rules file provided by usb-modeswitch-data. Along with the /lib/udev/usb_modeswitch and the /usr/sbin/usb_modeswitch binary, it should trigger the flip-flop switch when the device is plugged in. If this method doesn't work, please go to usb_modeswitch homepage: http://www.draisberghof.de/usb_modeswitch/ … and follow instructions. Once you have a working method for you device, please report it there: in any case, I don't intend to divert much from upstream. -- Didier Raboud Tue, 12 Jan 2010 14:33:53 +0100 debian/usb-modeswitch.prerm0000664000000000000000000000026212317322435013176 0ustar #!/bin/sh set -e if [ "$1" = remove ]; then # Also remove the runtime files. rm -f /var/lib/usb_modeswitch/last_seen rm -f /var/lib/usb_modeswitch/bind_list fi #DEBHELPER# debian/changelog0000664000000000000000000005624512320543277011063 0ustar usb-modeswitch (2.1.1+repack0-1ubuntu1) trusty; urgency=medium * Merge with Debian unstable; remaining changes: (LP: #1280546) - patch to rewrite dispatcher to C. - debian/patches/redirect_dispatcher_output.patch: redirect all dispatcher output when called from udev to /dev/null. - Replace libjim depends with libpipeline-dev, libudev-dev. * Fixed some bugs in dispatcher rewrite. (LP: #1155975) -- Mathieu Trudel-Lapierre Mon, 07 Apr 2014 11:39:41 -0400 usb-modeswitch (2.1.1+repack0-1) unstable; urgency=medium * New 2.1.1 upstream release - "Interface" parameter was not working as expected, fixed (LP: #1261923) * Repack the upstream tarball: - Drop the code copy of jimtcl. * Bump usb-modeswitch-data depends to 20140327 * Append CPPFLAGS to CFLAGS -- Didier Raboud Tue, 01 Apr 2014 13:28:36 +0200 usb-modeswitch (2.1.0+repack0-1) unstable; urgency=medium * New 2.1.0 upstream release - -I flag meaning reversed, default is to skip SCSI inquiry * Repack the upstream tarball: - Drop the code copy of jimtcl. * Bump usb-modeswitch-data depends to 20140129 * Bump Standards-Version to 3.9.5 without changes needed * Add get-orig-source target to do the automatic repacking -- Didier Raboud Mon, 10 Feb 2014 12:58:03 +0100 usb-modeswitch (2.0.1+repack0-2) unstable; urgency=low * Update the systemd patch to change the systemctl path to fit Debian's (Closes: #725394) - thanks to Ralf Jung -- Didier Raboud Mon, 30 Sep 2013 10:57:27 +0200 usb-modeswitch (2.0.1+repack0-1) unstable; urgency=low * New 2.0.1 upstream release - Switched to libusb1.0 - Major code and debug output cleanup - Man page corrections and additions - Experimental systemd and upstart integration * Repack the upstream tarball: - Drop the code copy of jimtcl. * Refresh patches for 2.0.1 * Update Build-Depends to add libusb-1.0-0-dev and pkg-config * Incorporate support for upstart and systemd - patch the dispatcher to correctly check if upstart or systemd are running - install the upstart init file and the systemd service file -- Didier Raboud Wed, 18 Sep 2013 19:53:11 +0200 usb-modeswitch (1.2.6+repack0-1) unstable; urgency=low * New 1.2.6 upstream release - Several changes to streamline compiling as part of larger projects, mostly in Makefile; - fix compiler warnings appearing in certain build environments; - new Quanta procedure for Quanta 1K3 LTE; - fix for error with cascaded hubs in dispatcher script. * Repack the upstream tarball: - Drop the code copy of jimtcl. * Replace 04_cope_with_repack patch by one to to use the package-provided jimsh and libjim. * Bump usb-modeswitch-data depends to 20130607. -- Didier Raboud Sat, 08 Jun 2013 18:07:17 +0200 usb-modeswitch (1.2.5+repack0-2) unstable; urgency=low * Upload to unstable. -- Didier Raboud Mon, 06 May 2013 17:36:17 +0200 usb-modeswitch (1.2.5+repack0-1) experimental; urgency=low * New 1.2.5 upstream release - 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). * Repack the upstream tarball: - Drop the code copy of jimtcl. * Bump usb-modeswitch-data depends to 20121109. * Bump Standards-Version to 3.9.4 and debhelper B-D to 9 without changes needed. -- Didier Raboud Tue, 20 Nov 2012 10:45:07 +0100 usb-modeswitch (1.2.4+repack0-1) experimental; urgency=low * New 1.2.4 upstream release - 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. * Repack the upstream tarball: - Drop the code copy of jimtcl. * Drop patch to disable logging as was (temporarily) enabled in 1.2.3. * Refresh patches. -- Didier Raboud Tue, 28 Aug 2012 11:13:00 +0200 usb-modeswitch (1.2.3+repack0-1ubuntu3) quantal; urgency=low * debian/patches/dispatcher-c-rewrite.patch: correct the "search" for modprobe to properly figure out whether it's available from PATH. (LP: #990337) -- Mathieu Trudel-Lapierre Mon, 08 Oct 2012 19:39:48 -0400 usb-modeswitch (1.2.3+repack0-1ubuntu2) precise; urgency=low * debian/patches/dispatcher-c-rewrite.patch: fix check_success() checks for the presence of vendor and product IDs to allow modeswitch to correctly be reported as successful when a verification is required. (LP: #935230) -- Mathieu Trudel-Lapierre Wed, 07 Mar 2012 10:48:42 -0500 usb-modeswitch (1.2.3+repack0-1ubuntu1) precise; urgency=low * Merge with Debian; remaining changes: - debian/control: Drop the dependency on tcl and Build-Depends on jimsh/libjim (another tcl implementation). - debian/control: add libudev-dev and libpipeline-dev to Build-Depends. - debian/patches/dispatcher-c-rewrite.patch: rewrite the dispatcher in C, to be able to drop the Tcl dependencies. * debian/patches/dispatcher-c-rewrite.patch: adapt C rewrite patch to take in the changes from 1.2.1-1.2.3. * debian/patches/redirect_dispatcher_output.patch: redirect all dispatcher output when called from udev to /dev/null. -- Mathieu Trudel-Lapierre Thu, 09 Feb 2012 10:43:17 -0500 usb-modeswitch (1.2.3+repack0-1) unstable; urgency=medium * New 1.2.3 upstream release. - Fixed two bugs both causing the embedded-jimsh install variant of the dispatcher crash; (Closes: #656688) - Fixed some "regexp" incompatibilities with Debian's libjim. (Closes: #656063) * Repack the upstream tarball: - Drop the code copy of jimtcl. * Rise urgency to medium as this new upstream release fixes two RC bugs. * Add patch to disable logging as enabled in 1.2.3 upstream release. -- Didier Raboud Mon, 30 Jan 2012 18:16:11 +0100 usb-modeswitch (1.2.2+repack0-1) unstable; urgency=low * New 1.2.2 upstream release. - Fixed bad bug preventing mode switch for devices using TargetClass (Closes: #656248); * Repack the upstream tarball: - Drop the code copy of jimtcl. -- Didier Raboud Thu, 19 Jan 2012 11:27:49 +0100 usb-modeswitch (1.2.1+repack0-1) unstable; urgency=low * New 1.2.1 upstream release. * Repack the upstream tarball: - Drop the code copy of jimtcl. * Refresh patches. × 03_use_udev_specifics.patch: Refresh. -- Didier Raboud Mon, 02 Jan 2012 21:27:00 +0100 usb-modeswitch (1.2.0+repack0-1ubuntu1) precise; urgency=low * Merge with Debian testing; remaining changes: - debian/control: Drop the dependency on tcl and Build-Depends on jimsh/libjim (another tcl implementation). - debian/control: add libudev-dev and libpipeline-dev to Build-Depends. - debian/patches/dispatcher-c-rewrite.patch: rewrite the dispatcher in C, to be able to drop the Tcl dependencies. * debian/patches/dispatcher-c-rewrite.patch: - Adapt to follow the changes introduced in version 1.2.0. - Move bind_list, current_cfg and other state files to /run. - Fix handling parameters when the dispatcher is called manually instead of via udev. (LP: #829519) -- Mathieu Trudel-Lapierre Mon, 05 Dec 2011 15:00:29 -0500 usb-modeswitch (1.2.0+repack0-1) unstable; urgency=low * New 1.2.0 upstream release: - 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 (Closes: #629371); * Repack the upstream tarball: - Drop the code copy of jimtcl. * Refresh patches: × 03_use_udev_specifics.patch: Refresh. + 04_cope_with_repack.patch: Add. - wait_half_second_non_scsi.patch: was from upstream; drop. * Link usb-modeswitch-dispatcher against libjim. - Add Build-Depends on jimtcl and libjim-dev. - Drop Depends on tcl/tclsh. - Use explicit makefile targets. * Bump debhelper compat to 9, for auto- buildflags. - Bump debhelper B-D to 8.9.0~. - Add source lintian override. -- Didier Raboud Mon, 24 Oct 2011 09:50:41 +0200 usb-modeswitch (1.1.9-2) unstable; urgency=low * Patch the tcl dispatcher to wait some time with non-scsi-needing devices. (Closes: #637972) -- Didier Raboud Tue, 16 Aug 2011 23:04:57 +0200 usb-modeswitch (1.1.9-1ubuntu3) oneiric; urgency=low * debian/patches/dispatcher-c-rewrite.patch: fix crash in failing to match devices with config lists before SCSI attributes are checked. (LP: #824147) * debian/patches/03_use_udev_specifics.patch: avoid failing if usb_modeswitch gets called with --symlink in udev by removing the tclsh call; also redirect all output from these calls to /dev/null (we don't need it anyway). -- Mathieu Trudel-Lapierre Mon, 22 Aug 2011 11:19:11 -0400 usb-modeswitch (1.1.9-1ubuntu2) oneiric; urgency=low * Drop the dependency on tcl, which was the whole point of the C rewriting exercise! :) -- Steve Langasek Tue, 09 Aug 2011 13:13:06 -0700 usb-modeswitch (1.1.9-1ubuntu1) oneiric; urgency=low * debian/patches/dispatcher-c-rewrite.patch: rewrite the dispatcher binary in C to be able to drop Tcl from the CD. See https://blueprints.launchpad.net/ubuntu/+spec/desktop-o-cdspace. * debian/control: add libudev-dev and libpipeline-dev to Build-Depends, as new requirements for the C version of usb_modeswitch_dispatcher. -- Mathieu Trudel-Lapierre Tue, 09 Aug 2011 15:11:17 -0400 usb-modeswitch (1.1.9-1) unstable; urgency=low * New 1.1.9 upstream version: × Refresh 03_use_udev_specifics.patch. -- Didier Raboud Sun, 07 Aug 2011 16:59:11 +0200 usb-modeswitch (1.1.8-1) unstable; urgency=low * New 1.1.8 upstream version: - Rewrite Debian's "override from /etc" code (Closes: #630081, thanks to Alex Hermann). - Fix boot-time switching regression (Closes: #629371, thanks to Christian Kastner). * Drop patches obsoleted by 1.1.8: - 01_extract_to_var_lib_not_tmp - 02_allow_override_from_etc * Drop device_reference.txt from docs as upstream dropped it. * Bump Standards-Version to 3.9.2 without changes needed. -- Didier Raboud Mon, 20 Jun 2011 10:12:34 +0200 usb-modeswitch (1.1.7-1) unstable; urgency=low * New 1.1.7 upstream version. * Patches: + Add 01_extract_to_var_lib_not_tmp.patch to extract temporary files to /var/lib/usb_modeswitch instead of /tmp. + Add 02_allow_override_from_etc.patch to permit overriding packed configurations with files within /etc/usb_modeswitch.d/. - Remove 02_mp_correct_hyphens.patch: merged upstream. - Remove 04__use_var_lib_not_etc.patch: merged upstream. - Remove 05_fixed_configuration_switching_races.patch: was an upstream backport. - Remove 06_umdp_archive_in_usr.patch: Obsoleted by upstream. * Replace usb-modeswitch-data-packed dependency by a versioned dependency on usb-modeswitch-data (copes with -packed package removal). * Add a postinst maintainer script to move past runtime files to their new (and correct) location under /var/lib/. * Also bump debhelper compat (was forgotten). -- Didier Raboud Tue, 15 Mar 2011 17:57:00 +0100 usb-modeswitch (1.1.6-2) unstable; urgency=low * Upload to unstable. * Use new dh_installdeb maintscript possibility: - Bump debhelper Build-Depends to 8.1.0. - Add Pre-Depends on ${misc:Pre-Depends} to usb-modeswitch. - Remove usb-modeswitch.preinst. - Add usb-modeswitch.maintscript. * Use my @d.o address and remove the DMUA flag. -- Didier Raboud Wed, 09 Feb 2011 14:11:13 +0100 usb-modeswitch (1.1.6-1) experimental; urgency=low * New 1.1.6 upstream version. * Add a prerm maintainer script to remove leftover runtime files. * Update package relationships to allow the install of usb-modeswitch- data-packed. * Patches: + 06_umdp_archive_in_usr.patch: Add to search for the compressed archive in /usr/share/usb-modeswitch-data/ x Refresh all others. * Bump Standards to 3.9.1 without changes needed. -- Didier Raboud Tue, 04 Jan 2011 17:22:40 +0100 usb-modeswitch (1.1.5-1) experimental; urgency=low * New 1.1.5 upstream version. * Patches: - 01_no_bash_before_tcl.patch: remove, included upstream. x Refresh all others. -- Didier Raboud Wed, 01 Dec 2010 15:59:47 +0100 usb-modeswitch (1.1.4-2) unstable; urgency=low * Fix configuration switching race (LP: #673435) -- Didier Raboud Thu, 11 Nov 2010 14:52:43 +0100 usb-modeswitch (1.1.4-1) unstable; urgency=low * Patches: + 04_use_var_lib_not_etc.patch : Add to put runtime files in /var/lib. * Release to unstable, thanks to the Release Team approval. -- Didier Raboud Mon, 30 Aug 2010 18:30:37 +0200 usb-modeswitch (1.1.4-1~exp0) experimental; urgency=high * New 1.1.4 upstream version; relevant changes: - The package should work at boot time now (cold and warm); (Closes: #591765, #591722) - wrapper fix for the symlink feature: handling of multiple interrupt ports was incomplete; (Closes: #587776) - wrapper does not longer use a temporary file for the symlink feature (security considerations, Marco d'Itri) (Closes: #591761) - no udev rules grep'ing (Closes: #591760) * Patches: + 01_no_bash_before_tcl.patch : add to avoid one more waiting. + 02_mp_correct_hyphens.patch: refresh. + 03_use_udev_specifics.patch: avoid code duplication by using udev specific hotplug functions. * Set urgency to high as new upstream fixes security bug. * Correct documentation glitches in debian/README.Debian (Closes: #590903) -- Didier Raboud Tue, 24 Aug 2010 00:07:38 +0200 usb-modeswitch (1.1.3-1) unstable; urgency=low * New 1.1.3 upstream version; relevant changes: - Small additions in Makefile (install with -D) => Remove patch 01_mf_install_path.patch - Changes in option handling (NO MORE DEFAULT CONFIG FILE!) and help text => Remove that conffile on upgrade, to avoid misunderstanding. => Include the device_reference.txt as docs. - Wrapper now ignores package manager leftovers in config folder => Remove patch 03_filter_undesired_rules.patch - Replaced bash-specific syntax in wrapper => Remove patch 04_fix_bashism.patch * Patches: + 02_mp_correct_hyphens.patch: refresh. -- Didier Raboud Wed, 23 Jun 2010 10:43:35 +0200 usb-modeswitch (1.1.2-3) unstable; urgency=low * Add 04_fix_bashisms.patch to fix bashism in usb_modeswitch.tcl (Closes: #581143) -- Didier Raboud Tue, 18 May 2010 14:24:17 +0200 usb-modeswitch (1.1.2-2) unstable; urgency=low * Add 03_filter_undesired_rules.patch to filter out undesired files (Closes: #579981) -- Didier Raboud Mon, 03 May 2010 11:37:08 +0200 usb-modeswitch (1.1.2-1) unstable; urgency=low * New 1.1.2 upstream version. - 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 * Update 01_mf_install_path.patch and 02_mp_correct_hyphens.patch * Don't use Debian-specific revisions in debian/control. -- Didier Raboud Mon, 19 Apr 2010 13:49:19 +0200 usb-modeswitch (1.1.1-1) unstable; urgency=low * New 1.1.1 upstream version. - 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 - Bug fixed in SuccessCheck logic - Minor flow alignments and fixes - Debian: + Update patches + Update preinst to not drop /etc/usb_modeswitch.conf anymore + Update usb-modeswitch.install to install the conffile * Now that I am a DM, add DMUA field to debian/control. Thanks go to Patrick Matthäi for the sponsoring! -- Didier Raboud Thu, 18 Mar 2010 11:08:06 +0100 usb-modeswitch (1.1.0-2) unstable; urgency=low * Drop the udev rules file (shipped in usb-modeswitch-data). Thanks to Sven-Haegar Koch for the report! (Closes: #567438) -- Didier Raboud Fri, 29 Jan 2010 09:24:41 +0100 usb-modeswitch (1.1.0-1) unstable; urgency=low The "All your base are belong to us" release. It was great to collaborate with upstream on that release. Thanks Josh! * New 1.1.0 upstream version. - Debian manpage got included, drop docbook-to-man. - Merge the two wrappers and put them in /lib/udev/. * Add a lintian override for the polyglot in /lib/udev/ * Update debian/watch to new upstream tarball naming. * Bump dependency on -data to 20100127-1. * Suggest comgt instead of gcom. * Patches: + 01_mf_install_all_path.patch Add - Ensures that the directories are created before accessing them - Disables the udev rules reload + 02_mp_correct_hyphens.patch Add to correct hyphen-used-as-minus-sign on the manpage. - 03_build_system_to_policy.patch Merged upstream. - 05_move_wrappers_to_usr.patch Merged upstream. -- Didier Raboud Thu, 28 Jan 2010 15:17:38 +0100 usb-modeswitch (1.0.7-1) unstable; urgency=low * New 1.0.7 upstream version (Closes: #563527). - Update the manpage - Split source in binary and data packages. * Patches: - 01_correct_broken_huawei_conf.patch Removed. + 03_build_system_to_policy.patch Refreshed. - 04_convert_umconf_to_unicode.patch Removed. + 05_move_wrappers_to_usr.patch Refreshed. - 06_disables_rules.patch Removed. * Add a preinst to delete configuration files forgotten in previous releases - Also delete /etc/udev/rules.d/80-usb_modeswitch.rules. - Delete only the data files not included in the -data upload * Update README.Debian. -- Didier Raboud Tue, 12 Jan 2010 15:58:14 +0100 usb-modeswitch (1.0.5-1) unstable; urgency=low * New 1.0.5 upstream version - Configurations are now stored in independent files under /etc/usb_modeswitch.d/ - The devices detection is greatly improved by having a broader set of detection fields (Closes: #527122). - Two new binaries: usb_modeswitch_sh and usb_modeswitch_tcl. * Adaptation to new upstream: - Add tclsh to Depends. - Drop mkrules.py and thus python B-D (Closes: #535445). - Update manpage to 1.0.5 (add --version) * Patches: - 01_correct_broken_huawei_conf.patch : refresh. - 02_umconf_is_no_exec.patch : remove, merged upstream. - 03_build_system_to_policy.patch : refresh. - 04_convert_umconf_to_unicode.patch : Add to get everything in utf-8. Thanks to W. Martin Borgert. - 05_move_wrappers_to_usr.patch : Add. Moves wrappers to specific /usr/share/usb_modeswitch - 06_disables_rules.patch : Add to get the udev rules "opt-in" (See NEWS.Debian) - Add headers to make them all comply to DEP-3 Patch Tagging Guidelines. * Bump Standards-Version to 3.8.3 - Add README.source. * Document the major changes in NEWS.Debian * Switch to source format 3.0 (quilt). Drop quilt Build-Dep and dh snippet. -- Didier Raboud Tue, 03 Nov 2009 11:02:31 +0100 usb-modeswitch (1.0.2-1) unstable; urgency=low * New 1.0.2 Upstream Version - New devices: + EpiValley SEC-7089 (featured by Alegro and Starcomms / iZAP) + ST Mobile Connect HSUPA USB Modem - The command line options have changed: + On/off flags don't require arguments anymore + long option names changed to standard format (e.g. --HuaweiMode to --huawei-mode) - Code cleanup - Added device inquiry - Send and response endpoints now autoselected * Fix manpage accordingly. * Update mkrules.py accordingly with cleaner coding style, thanks to Kris Warkentin. * Add debian/NEWS with a clear warning about the command line changes. * Fix "Broken generated udev rules for Huawei devices." by adding patch 01_correct_broken_huawei_conf.patch (Closes: #530788). - Thus adding quilt to Build-Depends and tweak debian/rules accordingly. - Thanks Stephen Depooter. * debian/rules: Redo from scratch using debhelper tiny style. - Thus bump Build-Depends on debhelper to > 7.0.50. - Use upstream's build system. - Add patch 02_umconf_is_no_exec.patch. - Add patch 03_build_system_to_policy.patch to accept DEB_BUILD_OPTIONS (noopt, nostrip). * Switch packaging to git under Alioth's collab-maint - Add Vcs-{Git,Browser} to debian/control. * Bump Standards to 3.8.2, no changes needed. -- Didier Raboud Tue, 23 Jun 2009 17:41:06 +0200 usb-modeswitch (0.9.7-1) unstable; urgency=low * 0.9.7 new upstream release. "Updated SonyMode, MD 400 now stable; automatic default endpoint detection from Andrew Bird." - Sony Ericsson MD400 now working - Automatic bulk endpoint detection * debian/copyright - Point to GPL version 2, thanks to lintian --pedantic. -- Didier Raboud Thu, 16 Apr 2009 11:34:47 +0200 usb-modeswitch (0.9.7~beta1-1) experimental; urgency=low * 0.9.7beta new upstream release. "Major code clean up, optional success control (both suggested by Daniel Cooper), new devices" * New build system (not used in Debian yet). * New option to enable success control (--success) - Manpage updated * New devices - Option iCON 210,401 - Vodafone K3760 - ZTE MF636 (aka "Telstra / BigPond 7.2 Mobile Card") - Sierra Wireless Compass 597 - MobiData MBD-200HU - Hyundai Mobile MB-810 * Bump Standards to 3.8.1. No changes needed. -- Didier Raboud Mon, 30 Mar 2009 19:44:55 +0200 usb-modeswitch (0.9.6-2) unstable; urgency=low * debian/copyright: Make clear that the packaging is GPLv2+, as usb-modeswitch itself * debian/mkrules.py: Generate udev rules based on /etc/usb-modeswitch.conf * Add gcom and wvdial to Suggests. -- Didier Raboud Sat, 14 Feb 2009 12:32:43 +0100 usb-modeswitch (0.9.6-1) unstable; urgency=low * Initial release (Closes: #453732) * Description taken from the ITP, thanks to Aurélien GÉRÔME. -- Didier Raboud Tue, 03 Feb 2009 14:11:19 +0100 debian/watch0000664000000000000000000000013012317322435010215 0ustar version=3 http://www.draisberghof.de/usb_modeswitch/ usb-modeswitch-([\d.]*)\.tar\.bz2 debian/source.lintian-overrides0000664000000000000000000000013112317322435014045 0ustar # This is needed for "auto-buildflags" package-needs-versioned-debhelper-build-depends 9 debian/README.source0000664000000000000000000000070412317322435011352 0ustar This package uses quilt for upstream source code patch management. Please read /usr/share/doc/quilt/README.source for more information how to apply, unapply, add, modify or remove patches. Please note that /usr/share/doc/quilt/README.source is only available in quilt version 0.46-4.1 or later. To use quilt with dh: - add debhelper (>= 7.2.14) build dependency - add quilt (>= 0.46-7) build dependency - use quilt addon (e.g. dh --with quilt $@) debian/source/0000775000000000000000000000000012320604540010464 5ustar debian/source/format0000664000000000000000000000001412317322435011700 0ustar 3.0 (quilt) debian/NEWS0000664000000000000000000000516312317322435007676 0ustar usb-modeswitch (1.0.5-1) unstable; urgency=low UPSTREAM: Version 1.0.5 introduces two major changes. * The devices configuration is split: - /etc/usb_modeswitch.conf is dropped in favor of files in /etc/usb_modeswitch.d/ - This means that the customisation or new devices that were defined in /etc/usb_modeswitch.conf will be _LOST_. + Normally, there no need anymore to play with comments and such to only get the correct devices switched. * The detection of devices is greatly improved: - /etc/udev/rules.d/usb_modeswitch.rules (Debian-specific) is dropped in favor of /etc/udev/rules.d/80-usb_modeswitch.rules (from upstream). These rules call /usr/share/usb_modeswitch/usb_modeswitch.{sh,tcl} to handle the automagical detection and switching of the devices. - This means that any device selection done by comment-tweak in the Debian-specific .rules file will be lost. + But it is normally replaced by a superior and more universal device detection. Finally, if you had modified /etc/usb_modeswitch.conf to insert home-brewn tweaks, you will need to migrate these tweaks to /etc/usb_modeswitch.d/* files. But if you had only selected alternative devices in /etc/udev/usb_modeswitch.rules, you should normally have nothing to do, as these alternatives should now be detected (read below). DEBIAN: Version 1.0.5-1 now makes the rules "opt-in" * udev (from version 146-1) now ships a modem-modeswitch program that does what usb-modeswitch used to do. To ensure that the transition to this udev facility is smooth for everyone, the usb-modeswitch program is kept, but with an opt-in /etc/udev/rules.d/80-usb_modeswitch.rules. * Thus if modem-modeswitch from udev (>= 146-1) works for your device, you can uninstall usb-modeswitch. * A contrario, if modem-modeswitch doesn't switch your device, please file a bug against udev and edit /etc/udev/rules.d/80-usb_modeswitch.rules to enable it. -- Didier Raboud Tue, 03 Nov 2009 11:02:40 +0100 usb-modeswitch (1.0.2-1) unstable; urgency=low For the 1.0.0 release, the command line options have changed in two ways from their behavior in the 0.9.* versions: * On/off flags don't require arguments anymore (e.g. '-H' instead of '-H 1') * long option names changed to standard format (e.g. --huawei-mode instead of --HuaweiMode). The old options are _not_ recognised and _will_ make usb_modeswitch fail. So check your scripts and everything that might be using these command lines. -- Didier Raboud Thu, 11 Jun 2009 16:59:03 +0200 debian/docs0000664000000000000000000000000712317322435010042 0ustar README debian/control0000664000000000000000000000277312317323034010602 0ustar Source: usb-modeswitch Section: comm Priority: extra Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Didier Raboud Build-Depends: debhelper (>= 9), libusb-1.0-0-dev, pkg-config, libpipeline-dev, libudev-dev, Standards-Version: 3.9.5 Homepage: http://www.draisberghof.de/usb_modeswitch/ Vcs-Git: git://git.debian.org/collab-maint/usb-modeswitch.git Vcs-Browser: http://git.debian.org/?p=collab-maint/usb-modeswitch.git Package: usb-modeswitch Architecture: any Pre-Depends: ${misc:Pre-Depends} Depends: ${shlibs:Depends}, ${misc:Depends}, usb-modeswitch-data (>= 20140327) Breaks: usb-modeswitch-data (<< 20100127) Suggests: comgt, wvdial Description: mode switching tool for controlling "flip flop" USB devices Several new USB devices have their proprietary Windows drivers onboard, especially WAN dongles. When plugged in for the first time, they act like a flash storage and start installing the driver from there. If the driver is already installed, the storage device vanishes and a new device, such as an USB modem, shows up. This is called the "ZeroCD" feature. . On Debian, this is not needed, since the driver is included as a Linux kernel module, such as "usbserial". However, the device still shows up as "usb-storage" by default. usb-modeswitch solves that issue by sending the command which actually performs the switching of the device from "usb-storage" to "usbserial". . This package contains the binaries and the brother scripts. debian/usb-modeswitch.docs0000664000000000000000000000000712317322435012776 0ustar README debian/rules0000775000000000000000000000173612317604071010260 0ustar #!/usr/bin/make -f export DEB_CFLAGS_MAINT_APPEND = $(shell dpkg-buildflags --get CPPFLAGS) %: dh $@ override_dh_auto_build: dh_auto_build -- shared # This allows to select what I want instead of deleting what I don't override_dh_auto_install: DESTDIR=debian/tmp/ make install-shared get-orig-source: set -e;\ quilt pop -af || true ;\ rm -Rf .pc ;\ git import-orig --no-pristine-tar --no-merge --uscan ;\ git checkout upstream-repack ;\ utag=`git describe --exact-match heads/upstream | sed -e 's#^upstream/##'` ;\ git merge upstream/$$utag -m "Merge upstream $$utag version" ;\ urtag=$$utag+repack0 ;\ git tag upstream/$$urtag -m "Upstream repacked $$utag version" ;\ git archive --format=tar --prefix=usb-modeswitch-$$urtag/ upstream/$$urtag | xz -6e > ../usb-modeswitch_$$urtag.orig.tar.xz ;\ pristine-tar commit ../usb-modeswitch_$$urtag.orig.tar.xz upstream/$$urtag ;\ git checkout master ;\ git merge upstream/$$urtag -m "Merge upstream-repacked $$urtag version" debian/usb-modeswitch.maintscript0000664000000000000000000000136312317322435014411 0ustar rm_conffile "/etc/udev/rules.d/usb_modeswitch.rules" 1.0.7-1 rm_conffile "/etc/udev/rules.d/80-usb_modeswitch.rules" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.d/05c6:1000:?" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.d/19d2:2000:?:?:?" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.d/19d2:2000:?:?" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.d/19d2:2000:?" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.d/19d2:2000:?:?:?" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.d/19d2:2000:?:?" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.d/19d2:2000:?" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.d/19d2:2000:?:?:?" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.d/19d2:2000:?:?" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.d/19d2:2000:?:?:?" 1.0.7-1 rm_conffile "/etc/usb_modeswitch.setup" 1.1.3-1 debian/copyright0000664000000000000000000000263412317322435011132 0ustar This package was debianized by: Didier Raboud on Fri, 30 Jan 2009 16:15:18 +0100 It was downloaded from: http://www.draisberghof.de/usb_modeswitch/ Copyright: Copyright 2007, 2008 Josua Dietze (mail to the name from "usb_modeswitch.c" at the domain "draisberghof.de"; please do not post the complete address to The Net !) or write a personal message through the forum to "Josh" License: This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This package is distributed in the hope that it will be useful, but WITHOUT 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL-2'. The Debian packaging is: Copyright (C) 2009, Didier Raboud and is licensed under the GPLv2+, as usb-modeswitch itself, see above. debian/patches/0000775000000000000000000000000012320604540010613 5ustar debian/patches/05_upstart_systemd_runtime_detection.patch0000664000000000000000000000202512317322502021213 0ustar Description: Detect if upstart or systemd are running, not if their corresponding configfiles are installed Also change the systemctl path to fit Debian's Author: Didier Raboud Origin: vendor Bug-Debian: http://bugs.debian.org/725394 Last-Update: 2013-10-07 --- a/usb_modeswitch.sh +++ b/usb_modeswitch.sh @@ -77,10 +77,10 @@ ( . /lib/udev/hotplug.functions wait_for_file /usr/sbin/usb_modeswitch_dispatcher - if [ -e "/etc/init/usb-modeswitch-upstart.conf" ]; then + if [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | /bin/grep -q upstart; then # Test if upstart is running exec /sbin/initctl emit --no-wait usb-modeswitch-upstart UMS_PARAM=$1 - elif [ -e "/etc/systemd/system/usb_modeswitch@.service" ]; then - exec /usr/bin/systemctl --no-block start usb_modeswitch@$1.service + elif [ -d "/run/systemd/system/" ]; then # Test if systemd is running + exec /bin/systemctl --no-block start usb_modeswitch@$1.service else exec /usr/sbin/usb_modeswitch_dispatcher --switch-mode $1 & fi debian/patches/03_use_udev_specifics.patch0000664000000000000000000000225712317322502016014 0ustar Description: Use udev-in-Debian specific tools for the waiting script This is inspired from alsa-utils' Author: Didier Raboud Origin: vendor Last-Update: 2013-09-17 --- a/usb_modeswitch.sh +++ b/usb_modeswitch.sh @@ -66,21 +66,17 @@ --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 + . /lib/udev/hotplug.functions + wait_for_file /usr/sbin/usb_modeswitch_dispatcher + exec usb_modeswitch_dispatcher $1 $2 2>>/dev/null fi exit 0 ;; esac exec 1<&- 2<&- 5<&- 7<&- ( -count=20 -while [ $count != 0 ]; do - if [ ! -e "/usr/sbin/usb_modeswitch_dispatcher" ]; then - sleep 1 - count=$(($count - 1)) - else + . /lib/udev/hotplug.functions + wait_for_file /usr/sbin/usb_modeswitch_dispatcher if [ -e "/etc/init/usb-modeswitch-upstart.conf" ]; then exec /sbin/initctl emit --no-wait usb-modeswitch-upstart UMS_PARAM=$1 elif [ -e "/etc/systemd/system/usb_modeswitch@.service" ]; then @@ -89,7 +85,5 @@ exec /usr/sbin/usb_modeswitch_dispatcher --switch-mode $1 & fi exit 0 - fi -done ) & exit 0 debian/patches/dispatcher-c-rewrite.patch0000664000000000000000000020576212317606546015713 0ustar From: Mathieu Trudel-Lapierre Subject: Rewrite the dispatcher in C to avoid requiring Tcl. Last-Update: 2011-12-05 --- Makefile | 5 usb_modeswitch_dispatcher.c | 2365 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2369 insertions(+), 1 deletion(-) Index: b/usb_modeswitch_dispatcher.c =================================================================== --- /dev/null +++ b/usb_modeswitch_dispatcher.c @@ -0,0 +1,2365 @@ +/* + Mode switching tool for controlling flip flop (multiple device) USB gear + Version 14.04, 2014/04/03 by Mathieu Trudel-Lapierre + + Created with initial help from: + "usb_modeswitch.tcl" by Joshua Dietze + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT 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 + +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RUN_DIR "/run/usb_modeswitch" +#define BIND_LIST "/run/usb_modeswitch/bind_list" +#define DEFAULT_TMPCONFIG "/run/usb_modeswitch/current_cfg" +#define LOGFILE_TEMPL "/var/log/usb_modeswitch.log" /* will be suffixed with a device ID */ +#define DB_DIR "/usr/share/usb_modeswitch" +#define DB_ETCDIR "/etc/usb_modeswitch.d" +#define BIN_DIR "/usr/sbin" +#define DEV_DIR_BASE "/sys/bus/usb/devices" +#define CONFIG_PACK_NAME "configPack.tar.gz" +#define MODPROBE "modprobe" +#define USB_STORAGE_DELAY_USE "/sys/module/usb_storage/parameters/delay_use" +#define MODULES_PATH "/lib/modules" +#define CDC_MBIM_DRIVER_PATH "kernel/drivers/net/usb/cdc_mbim.ko" +#define CDC_MBIM_SYS_PATH "/sys/bus/usb/drivers/cdc_mbim" +#define OSRELEASE_FILE "/proc/sys/kernel/osrelease" + +struct list_entry { + char *data; + struct list_entry *next; +}; + +struct conf_data { + char *dev_dir; + + char *config_pack_path; + char *config_name; + char *config_param; + + char *dev_param; + char *bus_param; + char *vendor_product_param; + + char *device_config; + + char *driver_id_path; + char *driver_module; + char *target_vendor; + char *target_product; + char *target_class; + int wait_before; + int target_config; + int driver_loading; + int check_success; + int no_mbim_check; + + char **config_list; +}; + +struct dev_attr { + char *attr; + char *value; +}; + +/* SCSI Vendor */ +#define sVe 0 + +/* SCSI Model */ +#define sMo 1 + +/* SCSI Revision */ +#define sRe 2 + +/* USB idVendor */ +#define idVendor 0 +/* USB idProduct */ +#define idProduct 1 + +/* USB Manufacturer */ +#define uMa 2 + +/* USB Product name */ +#define uPr 3 + +/* USB Serial No. */ +#define uSe 4 + +/* USB bNumConfigurations */ +#define bNumConfigurations 5 + +/* USB bConfigurationValue */ +#define bConfigurationValue 6 + +/* USB devnum */ +#define devnum 7 + +/* USB busnum */ +#define busnum 8 + +const char *config_places[] = { + "/etc/usb_modeswitch.conf", + "/etc/sysconfig/usb_modeswitch", + "/etc/default/usb_modeswitch", + NULL}; + +#define NUM_SCSI_ATTRS 3 /* always define to number of items in list below (minus NULL) */ +const char *scsi_attrs[] = { + "vendor", + "model", + "rev", + NULL }; + +#define NUM_USB_ATTRS 6 /* always define to number of items in list below (minus NULL) */ +const char *usb_attrs[] = { + "idVendor", + "idProduct", + "manufacturer", + "product", + "serial", + "bNumConfigurations", + "bConfigurationValue", + "devnum", + "busnum", + NULL }; + +int logging = 0; +int noswitching = 0; +int stordelay = 0; + +FILE *logfile = NULL; +char* device = NULL; +char* config_file = NULL; +char* device_iface_class = NULL; +struct conf_data config; +struct dev_attr *scsi[NUM_SCSI_ATTRS]; +struct dev_attr *usb[NUM_USB_ATTRS]; +struct timespec sleep_500; + +char* substring(const char* str, size_t begin, size_t len); +char* rtrim(char *s); +void free_list(struct list_entry *list); +char* join_path(char *base_path, char *add_path); +int parse_global_config(); +int has_interrupt (char* if_dir); +char* symlink_name (char* path); +void check_driver_bind (char* vid, char* pid); +int parse_device_config(char *device_config); +void remove_from_bind_list (char *id); +int add_to_list (char* name, char *id); +int in_bind_list (char *id); +void modeswitch_log(const char* format, ...); +void read_attrs(char *subsystem, struct dev_attr **dev_type, char **attr_list, char *dir); +void read_scsi_attrs(char *dir); +void read_usb_attrs(char *dir, char* ifdir); +void config_get_list(char *config_name); +char* config_get_config(char *config_name); +int match_device(char *config_name); +int read_pipeline (pipeline *p, int logging, int* no_driver_loading, int* found_ok); +void prepare_run_dir(); +int check_success(char *dir); +void set_storage_delay(int secs); +int check_mbim_available (void); +char* get_iface_ifdir (int iface); +int get_iface_ifclass (int iface); +int check_iface (int iface); + +static int min(int a, int b) +{ + return a > b ? b : a; +} + +int main(int argc, char* argv[]) +{ + regex_t reg; + regmatch_t match[3]; + char *val, *tmpval, *tmp; + char bus_id[PATH_MAX], *kernel_name = NULL; + char dev_top[PATH_MAX]; + char *udev_args = NULL; + char *msg, *sys_dir, *fname, *selected_config, *ifdir; + char line[PATH_MAX]; + int i; + int scsi_needed; + int no_data = 0; + int counter; + int no_driver_loading; + int found_ok; + int if_chk = 0; + int iface; + int mbim_cfg_no = 0; + int length; + pipeline *p; + FILE *rc; + glob_t path_glob; + int glob_status; + + sleep_500.tv_sec = 0; // 0 seconds... + sleep_500.tv_nsec = 500000000l; // ... plus .5 seconds. + + /* + * 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 known device interface by + * an udev rule. + */ + + if (argc > 1 && strcmp(argv[1], "--symlink-name") == 0 ) { + + if (argv[2] != NULL && (val = symlink_name(argv[2])) != NULL) + printf("%s\n", val); + else { + printf("Invalid parameters for --symlink-name.\n"); + _exit(1); + } + + _exit(0); + } + else if (argc > 1 + && (strcmp(argv[1], "--switch-mode") == 0 + || strcmp(argv[1], "--switch-systemd") == 0 + || strcmp(argv[1], "--switch-upstart") == 0)) { + udev_args = argv[2]; + } + else if (argc > 1 && strstr(argv[1], "--") == argv[1]) { + /* The argument passed isn't --symlink-name, but starts with --, + * so we don't care much about it, just display "usage". + */ + printf("Invalid arguments. This program should only be called via a udev rule.\n"); + _exit(1); + } + else if (argc < 3) { + /* Where there are no arguments passed to the dispatcher. */ + printf("Too few arguments. This program should only be called via a udev rule.\n"); + _exit(1); + } + parse_global_config(); + + if (access(DB_DIR, X_OK) != 0 && access(DB_ETCDIR, X_OK) != 0) { + modeswitch_log("\nError: no config database found in /usr/share or /etc. Exiting.\n"); + return 1; + } + + if (strcmp(argv[1], "--switch-systemd") == 0) { + modeswitch_log("\nStarted via systemd\n"); + } else if (strcmp(argv[1], "--switch-upstart") == 0) { + modeswitch_log("\nStarted via upstart\n"); + } else if (strstr(argv[1], "--switch") == NULL) { + modeswitch_log("\nNo command given. Exit\n"); + return 1; + } + + openlog("usb_modeswitch", LOG_PID, LOG_DAEMON); + + /* + * udev_args (argv[2]) contains the values provided from the udev rule separated by "/" + */ + if (!udev_args) { + modeswitch_log("\nNo data from udev. Exiting\n"); + return 1; + } + else { + if (stordelay > 0) + set_storage_delay(stordelay); + modeswitch_log("Raw args from udev: %s\n\n", udev_args); + } + + /* + * arg 0: the bus id for the device (udev: %b), often ommitted + * arg 1: the "kernel name" for the device (udev: %k) + * + * Used to determine the top directory for the device in sysfs + */ + if ((kernel_name = strchr(udev_args, '/')) != NULL) + kernel_name = kernel_name + 1; + else + kernel_name = ""; + + length = min(kernel_name - udev_args - 1, sizeof(bus_id) - 1); + strncpy (bus_id, udev_args, length); + bus_id[length] = '\0'; + + if (strlen(kernel_name) > 0 && strlen(bus_id) == 0) { + char *colon = strchr (kernel_name, ':'); + + modeswitch_log("Bus ID for device not given by udev.\n"); + modeswitch_log(" Trying to determine it from kernel name (%s) ...\n", kernel_name); + + if (colon) { + length = min(colon - kernel_name, sizeof(dev_top) - 1); + strncpy (dev_top, kernel_name, length); + dev_top[length] = '\0'; + } else { + strncpy(dev_top, kernel_name, sizeof(dev_top) - 1); + dev_top[sizeof(dev_top) - 1] = '\0'; + if (strchr(kernel_name, '-') != NULL && strchr(kernel_name, '.') != NULL) + if_chk = 1; + } + } + else if (strlen(bus_id) > 0) { + strcpy(dev_top, bus_id); + } + else { + modeswitch_log("No device number values given from udev! Exit.\n"); + return 1; + } + + if (!*dev_top) { + modeswitch_log("Could not determine top device dir from udev values! Exit.\n"); + return 1; + } + + asprintf(&config.dev_dir, "%s/%s", DEV_DIR_BASE, dev_top); + if (access(config.dev_dir, X_OK) != 0) { + modeswitch_log("Top device directory not found (%s)! Exit.\n", config.dev_dir); + return 1; + } + modeswitch_log("Use top device dir %s\n", config.dev_dir); + + /* Now reading the USB attributes */ + /* TODO: make this return a bool and check USB ID existance w/early out. */ + read_usb_attrs(config.dev_dir, NULL); + + /* also "read" scsi to initialize the data structures at least to empty strings */ + /* TODO: same as above. */ + read_scsi_attrs(config.dev_dir); + + if (usb[idVendor]->value == NULL || usb[idProduct]->value == NULL + || strlen(usb[idVendor]->value) + strlen(usb[idProduct]->value) < 8) { + modeswitch_log("USB IDs not found in sysfs tree. Exiting.\n"); + return 1; + } + + iface = 0; + if (if_chk) { + modeswitch_log("Check class of first interface ...\n"); + + iface = check_iface(0); + if (iface < 0) { + modeswitch_log(" No access to interface 0. Exit\n"); + return 1; + } else { + modeswitch_log(" Device is in install mode.\n"); + } + } + ifdir = get_iface_ifdir(iface); + + modeswitch_log("Use interface %s", ifdir); + + modeswitch_log("\n----------------\nUSB values from sysfs:\n"); + for (i = 0; i < NUM_USB_ATTRS; i++) { + modeswitch_log(" %s\t%s\n", usb[i]->attr, usb[i]->value); + } + modeswitch_log("----------------\n"); + + if (noswitching) { + modeswitch_log("\nSwitching globally disabled. Exit.\n"); + syslog(LOG_NOTICE, "usb_modeswitch: switching disabled, no action for %s:%s", + usb[0]->value, usb[1]->value); + return 0; + } + + if (strcmp(usb[bNumConfigurations]->value, "1") == 0) { + config.config_param = strdup("-u -1"); + modeswitch_log("bNumConfigurations is 1 - don't check for active configuration\n"); + } + else { + config.config_param = 0; + } + + if (usb[devnum]->value != NULL && strlen(usb[devnum]->value) > 0) + asprintf(&config.dev_param, "-g %s", usb[devnum]->value); + else + config.dev_param = strdup(""); + + if (usb[busnum]->value != NULL && strlen(usb[busnum]->value) > 0) + asprintf(&config.bus_param, "-b %s", usb[busnum]->value); + else + config.bus_param = strdup(""); + + /* + * Retrieve the full list of available configurations from the config pack, + * but also look for files matching the USB ID name in config directories, + * in /usr/share/usb-modeswitch and /etc/usb-modeswitch.d. + * + * 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. + */ + asprintf(&config.config_name, "%s:%s", usb[idVendor]->value, usb[idProduct]->value); + config_get_list(config.config_name); + + if (config.config_list == NULL) { + modeswitch_log("Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exit.\n"); + return 1; + } + + /* + * Look through the list of possible configurations available to find + * the last to match. This is to make sure we get an overriding config + * if one exists, or at least a config directly from the tarball. + * If nothing matches, the config_name is a new device that nothing else + * defines (the pack of configs, or files in /usr/share, or in /etc... + */ + selected_config = NULL; + for (i = 0; config.config_list[i] != NULL; i++) { + if (strstr(config.config_list[i], config.config_name) != NULL) { + selected_config = config.config_list[i]; + } + } + + scsi_needed = 0; + if (selected_config != NULL) { + int len = strlen (selected_config); + if (selected_config[len - 2] == ':' && + selected_config[len - 1] == 's') + scsi_needed = 1; + } + + if (!scsi_needed) + modeswitch_log("SCSI attributes not needed, move on.\n"); + + // TODO: factor this out to another function (including ^). + /* + * Getting the SCSI values via libusb results in a detached + * usb-storage driver. Not good for devices that want to be + * left alone. Fortunately, the sysfs tree provides the values + * too without need for direct access + * + * First we wait until the SCSI data is ready - or timeout. + * Timeout means: no storage driver was bound to the device. + * We run 20 times max, every half second (max. 10 seconds + * total) + * + * We also check if the device itself changes, probably + * because it was switched by the kernel (or even unplugged). + * Then we do simply nothing and exit quietly ... + */ + + i = 0; + while (scsi_needed == 1 && i < 20) { + nanosleep(&sleep_500, NULL); + i++; + modeswitch_log("waiting for storage tree in sysfs\n"); + + asprintf(&sys_dir, "%s/%s", config.dev_dir, kernel_name); + if (access(sys_dir, X_OK) != 0) { + /* Device is gone. Unplugged? Switched by kernel? */ + modeswitch_log("sysfs device tree is gone; exiting.\n"); + return 1; + } + + asprintf(&fname, "%s/product", config.dev_dir); + + if (access(fname, F_OK) == 0) { + modeswitch_log("file name exists: %s\n", fname); + } + else { + modeswitch_log("file name not readable: %s\n", fname); + continue; + } + + rc = fopen(fname, "r"); + fgets(line, PATH_MAX, rc); + val = strdup(rtrim(line)); + fclose(rc); + + if (strcmp(val, usb[uPr]->value) != 0) { + /* Device has just changed. Switched by someone else? */ + modeswitch_log("device has changed; exiting.\n"); + return 0; + } + free(val); + + /* Searching the storage/SCSI tree; might take a while. + * The SCSI tree in sysfs needs to all be there, so we'll slowly + * get through each of the subdirectories checking for existence: + * first the /sys/devices/xxx/host* directory; then its target* + * subdirectory. Only ever the first subdirectory with that name, + * for USB modems they shouldn't have more anyway. + */ + asprintf(&val, "%s/host*", sys_dir); + glob_status = glob(val, 0, NULL, &path_glob); + if (glob_status == 0 && path_glob.gl_pathv[0] != NULL) { + asprintf(&sys_dir, "%s/target*", path_glob.gl_pathv[0]); + globfree(&path_glob); + free(val); + glob_status = glob(sys_dir, 0, NULL, &path_glob); + if (glob_status == 0 && path_glob.gl_pathv[0] != NULL) { + if (strstr(path_glob.gl_pathv[0], "target") != NULL) { + asprintf(&sys_dir, "%s/*", path_glob.gl_pathv[0]); + globfree(&path_glob); + glob_status = glob(sys_dir, 0, NULL, &path_glob); + if (glob_status == 0 && path_glob.gl_pathv[0] != NULL) { + asprintf(&val, "%s/vendor", path_glob.gl_pathv[0]); + free(sys_dir); + asprintf(&sys_dir, "%s", path_glob.gl_pathv[0]); + if (access(val, F_OK) == 0) { + /* Finally SCSI structure is ready, get the values */ + read_scsi_attrs(sys_dir); + break; + } + } + globfree(&path_glob); + } + } + else + globfree(&path_glob); + } + else + globfree(&path_glob); + } + + if (scsi_needed) { + if (i == 20 && strlen(scsi[sVe]->value) == 0) { + modeswitch_log("SCSI tree not found; you may want to check if this path/file exists:\n"); + modeswitch_log("%s/vendor\n", sys_dir); + } + else { + modeswitch_log("----------------\nSCSI values from sysfs:"); + modeswitch_log(" vendor\t%s\n", scsi[sVe]->value); + modeswitch_log(" model\t%s\n", scsi[sMo]->value); + modeswitch_log(" revision\t%s\n", scsi[sRe]->value); + modeswitch_log("----------------\n"); + } + modeswitch_log("Waiting 2 secs. after SCSI device was added.\n"); + sleep(2); + } + else { + nanosleep(&sleep_500, NULL); + } + + /* + * If SCSI tree in sysfs was not identified, try and get the values + * from a (nonswitching) call of usb_modeswitch; this detaches the + * storage driver, so it's just the last resort. + */ + if (scsi_needed && strcmp(scsi[sVe]->value, "") == 0) { + char *hex_vendor, *hex_product; + char *attr_name, *attr_val; + const char *line2; + + asprintf(&hex_vendor, "0x%s", usb[idVendor]->value); + asprintf(&hex_product, "0x%s", usb[idProduct]->value); + asprintf(&tmpval, "%s/usb_modeswitch", BIN_DIR); + p = pipeline_new_command_args (tmpval, + "-v", hex_vendor, + "-p", hex_product, + "2>/dev/null", NULL); + free(tmpval); + free(hex_vendor); + free(hex_product); + pipeline_want_out (p, -1); + pipeline_start (p); + + regcomp(®, "(Vendor|Model|Revision) String: (.*?)", REG_EXTENDED); + + while ((line2 = pipeline_readline(p)) != NULL) { + tmp = rtrim(strdup(line2)); + if (regexec(®, line2, 3, match, 0) == 0) { + attr_name = substring(tmp, + match[1].rm_so, + match[1].rm_eo - match[1].rm_so); + attr_val = substring(tmp, + match[2].rm_so, + match[2].rm_eo - match[2].rm_so); + + i = -1; + if (strcmp(attr_name, "Vendor") == 0) + i = sVe; + else if (strcmp(attr_name, "Model") == 0) + i = sMo; + else if (strcmp(attr_name, "Revision") == 0) + i = sRe; + + if (i != -1) { + scsi[i]->attr = strdup(scsi_attrs[i]); + scsi[i]->value = attr_val; + } + } + } + regfree(®); + + modeswitch_log("SCSI values from usb_modeswitch:\n"); + modeswitch_log(" vendor\t%s\n", scsi[sVe]->value); + modeswitch_log(" model\t%s\n", scsi[sMo]->value); + modeswitch_log(" revision\t%s\n", scsi[sRe]->value); + } + modeswitch_log("\n"); + + /* General wait - this is important (1.2.3) */ + nanosleep(&sleep_500, NULL); + + /* + * Now check for a matching config file. Matching is done by match_device() + */ + selected_config = NULL; + for (i = 0; config.config_list[i] != NULL; i++) { + if (strstr(config.config_list[i], config.config_name) != NULL && + match_device(config.config_list[i])) { + selected_config = config.config_list[i]; + } + //else + // modeswitch_log("* no match, not switching with this config\n"); + } + + if (selected_config != NULL) { + if (config_get_config (selected_config) != NULL) { + parse_device_config (config.device_config); + if (config.wait_before == 0) { + modeswitch_log("! matched, now switching\n"); + } + else { + modeswitch_log("Delay time of %d seconds.\n", config.wait_before); + sleep(config.wait_before); + modeswitch_log(" wait is over, start mode switch.\n"); + } + + asprintf(&config.vendor_product_param, "-v %s -p %s", usb[idVendor]->value, usb[idProduct]->value); + + if (config.no_mbim_check == 0 + && strcmp(usb[bNumConfigurations]->value, "1") > 0) { + modeswitch_log("Device may have an MBIM configuration, check driver ...\n"); + if (check_mbim_available()) { + modeswitch_log(" driver for MBIM devices is available\n"); + modeswitch_log("Find MBIM configuration number ...\n"); + asprintf(&msg, + "%s/usb_modeswitch -j -Q %s %s %s 2>&1", + BIN_DIR, + config.bus_param, + config.dev_param, + config.vendor_product_param); + p = pipeline_new(); + pipeline_command_argstr (p, msg); + pipeline_want_out (p, -1); + pipeline_start (p); + tmp = pipeline_readline(p); + mbim_cfg_no = atoi(tmp); + if (mbim_cfg_no > 0) { + config.target_config = mbim_cfg_no; + config.driver_module = 0; + free(config.device_config); + config.device_config = strdup(DEFAULT_TMPCONFIG); + rc = fopen(config.device_config, "w"); + fprintf(rc, "Configuration=%d\n", mbim_cfg_no); + fclose(rc); + } else { + modeswitch_log(" No MBIM configuration found, switch to legacy modem mode\n"); + } + } else { + modeswitch_log(" no MBIM driver found, switch to legacy modem mode\n"); + } + } + + /* Now we are actually switching */ + no_driver_loading = 0; + found_ok = 0; + asprintf(&msg, + "%s/usb_modeswitch %s -D -s 20 -c %s %s %s %s %s 2>&1", + BIN_DIR, + logging ? "-W" : "-Q", + config.device_config, + config.config_param, + config.bus_param, + config.dev_param, + config.vendor_product_param); + p = pipeline_new(); + pipeline_command_argstr (p, msg); + pipeline_want_out (p, -1); + pipeline_start (p); + if (logging) { + modeswitch_log("Command to be run:\n%s\n\n", msg); + modeswitch_log("Verbose debug output of usb_modeswitch and libusb follows\n"); + modeswitch_log("(Note that some USB errors are expected in the process)\n"); + modeswitch_log("--------------------------------\n"); + } + free(msg); + no_data = read_pipeline(p, logging, &no_driver_loading, &found_ok); + if (logging) { + modeswitch_log("--------------------------------\n"); + modeswitch_log("(end of usb_modeswitch output)\n"); + } + pipeline_free(p); + if (strstr(config.device_config, "current_cfg") != NULL) { + unlink(config.device_config); + } + } + } + else { + modeswitch_log("Aargh! Config file missing for %s! Exiting.\n", config.config_name); + return 1; + } + + /* + * 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 (found_ok == 1) { + if (check_success(config.dev_dir)) { + modeswitch_log ("Mode switching was successful, found %s:%s (%s: %s)", + usb[idVendor]->value, usb[idProduct]->value, + usb[uMa]->value, usb[uPr]->value); + syslog(LOG_NOTICE, "usb_modeswitch: switched to %s:%s on %s/%s", + usb[idVendor]->value, usb[idProduct]->value, + usb[busnum]->value, usb[devnum]->value); + } + else { + modeswitch_log ("Target config not matching - current values are:\n"); + for (i = 0; i < NUM_USB_ATTRS; i++) { + modeswitch_log("\t%s: %s\n", + usb_attrs[i], + usb[i]->value != NULL ? usb[i]->value : ""); + } + modeswitch_log ("\nMode switching may have failed. Exit\n"); + return 1; + } + } + else { + if (access(config.dev_dir, X_OK) != 0) { + modeswitch_log("Device directory in sysfs is done! Something went wrong, abort.\n"); + return 1; + } + + /* Give the device another second if it's not fully back yet. */ + asprintf(&fname, "%s/idProduct", config.dev_dir); + if (access(fname, F_OK) != 0) { + sleep(1); + if (access(fname, F_OK) != 0) + sleep(1); + } + if (access(fname, F_OK) == 0) { + read_usb_attrs(config.dev_dir, ifdir); + } + } + + if (found_ok == 1 && config.driver_module != 0 && device_iface_class != NULL && strlen(device_iface_class) > 0) { + if (!strstr(device_iface_class, "ff")) { + config.driver_module = 0; + modeswitch_log (" No vendor-specific class found, skip driver check\n"); + } + } + + if (found_ok == 1 && config.driver_module) { + if(strlen(usb[idVendor]->value) > 4 && + strlen(usb[idProduct]->value) > 4 && + strcmp(usb[idVendor]->value, "0000") != 0 && + strcmp(usb[idProduct]->value, "0000") != 0) { + if (no_data) + modeswitch_log("Libusb1 bug prevented device searching, and device ID not found afterwards.\n"); + modeswitch_log("No vendor/product ID found or given, can't continue. Abort.\n"); + return 1; + } + + + /* wait for any drivers to bind automatically */ + sleep(1); + + modeswitch_log("Now check for bound driver ...\n"); + asprintf(&val, "%s/%s/driver", config.dev_dir, ifdir); + if (access(val, F_OK) != 0) { + modeswitch_log(" no driver has bound to interface 0 yet\n"); + + add_to_list("link_list", config.config_name); + + /* If device is known, the sh wrapper will take care, else: */ + if (in_bind_list(config.config_name) == 0) { + modeswitch_log("Device not in \"bind_list\" yet, bind it now\n"); + + /* load driver */ + check_driver_bind(usb[idVendor]->value, usb[idProduct]->value); + + /* Old/slow systems may take a while to create the devices */ + counter = 0; + while (access(val, F_OK) != 0) { + if (counter == 14) + break; + nanosleep(&sleep_500, NULL); + counter++; + } + if (counter == 14) { + modeswitch_log(" driver binding failed\n"); + } + else { + modeswitch_log(" driver was bound to the device\n"); + add_to_list("bind_list", config.config_name); + } + } + } + else { + modeswitch_log(" driver has bound, device is known\n"); + asprintf(&tmpval, "%s/%s/ttyUSB*", config.dev_dir, ifdir); + glob_status = glob(tmpval, 0, NULL, &path_glob); + if (glob_status == 0 && path_glob.gl_pathv[0] != NULL) { + add_to_list("link_list", config.config_name); + } + globfree(&path_glob); + free(tmpval); + } + free(val); + } + else { + /* Just in case "NoDriverLoading" was added after the first bind */ + remove_from_bind_list (config.config_name); + } + + if (config.driver_loading == 0) { + /* "NoDriverLoading" was set */ + modeswitch_log("Doing no driver check or bind for this device\n"); + } + + /* + * In newer kernels there is a switch to avoid the use of a device + * reset (e.g. from usb-storage) which would likely switch back + * a mode-switching device. + */ + if (found_ok == 1) { + modeswitch_log("Check for AVOID_RESET_QUIRK kernel attribute\n"); + asprintf(&fname, "%s/avoid_reset_quirk", config.dev_dir); + if (access(fname, F_OK) == 0) { + int fd = open(fname, O_WRONLY); + if (fd < 0) + perror("Error opening quirk file"); + if (write(fd, "1", 1) > 0) + modeswitch_log(" AVOID_RESET_QUIRK activated\n"); + else + perror(" Error setting the attribute"); + } + else + modeswitch_log(" not present in this kernel\n"); + } + + modeswitch_log("\nAll done, exit\n\n"); + + return 0; +} + +int read_pipeline (pipeline *p, int logging, int* no_driver_loading, int* found_ok) +{ + regex_t reg_ids, reg_nodata; + regmatch_t *match; + const char *tmp; + char *tmpval; + int no_data = 0; + + regcomp(®_ids, "ok:([0-9a-f]{4}):([0-9a-f]{4})", REG_EXTENDED | REG_ICASE); + regcomp(®_nodata, "ok:no_data", REG_EXTENDED | REG_ICASE); + match = malloc(sizeof(*match) * 3); + + while ((tmp = pipeline_readline(p)) != NULL) { + if (strstr(tmp, "ok:")) { + if (regexec(®_ids, tmp, 3, match, 0) == 0) { + tmpval = substring(tmp, + match[1].rm_so, + match[1].rm_eo - match[1].rm_so); + usb[idVendor]->value = strdup(tmpval); + tmpval = substring(tmp, + match[2].rm_so, + match[2].rm_eo - match[2].rm_so); + usb[idProduct]->value = strdup(tmpval); + *found_ok = 1; + } + if (regexec(®_nodata, tmp, 0, NULL, 0) == 0) { + no_data = 1; + *found_ok = 1; + } + if (strstr(tmp, "ok:busdev")) { + *found_ok = 1; + } + } + + if (logging) + modeswitch_log("%s", tmp); + } + + regfree(®_ids); + regfree(®_nodata); + free(match); + + return no_data; +} + +char* substring(const char* str, size_t begin, size_t len) +{ + if (str == 0 || strlen(str) == 0 || strlen(str) < begin || strlen(str) < (begin+len)) + return 0; + + return strndup(str + begin, len); +} + +char* rtrim(char *s) +{ + char* back = s + strlen(s); + + while(back >= s && isspace(*--back)); + + *(back+1) = '\0'; + + return s; +} + +void free_list(struct list_entry *list) +{ + struct list_entry *entry, *copy; + + entry = list; + while(entry != NULL && entry->data != NULL) { + copy = entry->next; + free(entry->data); + free(entry); + entry = copy; + } + + return; +} + +char* join_path(char *base_path, char *add_path) +{ + char *cleanpath = malloc(PATH_MAX); + char *token; + + memset(cleanpath, '\0', 1); + + token = strtok(base_path, "/"); + while (token != NULL) { + if (strcmp(token, "..") == 0) { + token = strtok(NULL, "/"); + continue; + } + else { + strcat(cleanpath, "/"); + strcat(cleanpath, token); + } + token = strtok(NULL, "/"); + } + token = NULL; + + token = strtok(add_path, "/"); + while (token != NULL) { + if (strcmp(token, "..") == 0) { + cleanpath = dirname(strdup(cleanpath)); + token = strtok(NULL, "/"); + continue; + } + else { + strcat(cleanpath, "/"); + strcat(cleanpath, token); + } + token = strtok(NULL, "/"); + } + return cleanpath; +} + +int parse_global_config() +{ + + char* temp_val; + char line[PATH_MAX]; + int i, j; + FILE *rc; + regex_t disable_switching_re, enable_logging_re, storagedelay_re; + regmatch_t *disable_switching_m = NULL, *enable_logging_m = NULL; + regmatch_t *storage_delay_m = NULL; + + for ( i = 0; config_places[i] != NULL; i++) { + j = access(config_places[i], F_OK); + if (j == 0) { + config_file = strdup(config_places[i]); + break; + } + } + + if (config_file == NULL) { + perror("no config file readable"); + return 1; + } + + rc = fopen(config_file, "r"); + + if (regcomp(&disable_switching_re, "^DisableSwitching[[:space:]]*=[[:space:]]*([^[:space:]]+)", REG_EXTENDED) != 0) { + return 1; + } + + if (regcomp(&enable_logging_re, "^EnableLogging[[:space:]]*=[[:space:]]*([^[:space:]]+)", REG_EXTENDED) != 0) { + regfree (&disable_switching_re); + return 1; + } + + if (regcomp(&storagedelay_re, "^SetStorageDelay[[:space:]]*=[[:space:]]*([^[:space:]]+)", REG_EXTENDED) != 0) { + regfree (&disable_switching_re); + return 1; + } + + disable_switching_m = malloc(sizeof(*disable_switching_m)*2); + enable_logging_m = malloc(sizeof(*enable_logging_m)*2); + storage_delay_m = malloc(sizeof(*storage_delay_m)*2); + + while (fgets(line, PATH_MAX, rc) != NULL) { + if (regexec(&disable_switching_re, line, 2, disable_switching_m, 0) == 0) { + temp_val = substring(line, + disable_switching_m[1].rm_so, + disable_switching_m[1].rm_eo - disable_switching_m[1].rm_so); + noswitching = atoi(temp_val); + free(temp_val); + } + if (regexec(&enable_logging_re, line, 2, enable_logging_m, 0) == 0) { + temp_val = substring(line, + enable_logging_m[1].rm_so, + enable_logging_m[1].rm_eo - enable_logging_m[1].rm_so); + logging = atoi(temp_val); + free(temp_val); + } + if (regexec(&storagedelay_re, line, 2, storage_delay_m, 0) == 0) { + temp_val = substring(line, + storage_delay_m[1].rm_so, + storage_delay_m[1].rm_eo - storage_delay_m[1].rm_so); + stordelay = atoi(temp_val); + free(temp_val); + } + } + fclose (rc); + + regfree(&disable_switching_re); + regfree(&enable_logging_re); + regfree(&storagedelay_re); + free(disable_switching_m); + free(enable_logging_m); + free(storage_delay_m); + + modeswitch_log("Use global config file: %s\n\n", config_file); + + return 0; +} + +int has_interrupt (char* if_dir) +{ + DIR *dir; + FILE *dev_type; + struct dirent *ent; + int found_tty = 0, ret_val = 0; + char *fname = NULL, line[PATH_MAX]; + + dir = opendir (if_dir); + if (dir != NULL) { + while ((ent = readdir (dir)) != NULL) { + if (strncmp (ent->d_name, "ttyUSB", 6)) { + found_tty = 1; + break; + } + } + closedir (dir); + + dir = NULL; + dir = opendir (if_dir); + if (dir != NULL && found_tty == 1) { + while ((ent = readdir (dir)) != NULL) { + if (strncmp (ent->d_name, "ep_", 3)) { + asprintf(&fname, "%s/%s/type", if_dir, ent->d_name); + dev_type = fopen(fname, "r"); + free (fname); + if (dev_type != NULL) { + while (fgets(line, PATH_MAX, dev_type) != NULL) { + if (strstr(line, "Interrupt")) { + modeswitch_log("\n %s has interrupt transfer type\n", if_dir); + ret_val = 1; + break; + } + } + /*R: break-before-close fd leak */ + fclose (dev_type); + } + } + if (ret_val == 1) { + break; + } + } + } + else { + ret_val = 0; + } + closedir (dir); + } + else { + //perror (""); + ret_val = -1; + } + + return ret_val; +} + +/*R: review done until here */ + +/* Checking for interrupt endpoint in ttyUSB port (lowest if there is + * more than one); if found, check for unused "gsmmodem[n]" name. + * Link for first modem will be "gsmmodem", then "gsmmodem2" and up + */ +char* symlink_name (char* path) +{ + char rawpath_buf[PATH_MAX], dev_rawpath_buf[PATH_MAX]; + char *msg, *dev_linkpath, *linkpath, *trimpath, *cleanpath; + char re_str[PATH_MAX]; + char *dev_top, *if_root, *if_dir; + char *symlink_name, *symlink_basename; + char *token, *w_token, *dev_dir; + int my_if; + int seen = 0; + int n_chars, right_port, i, idx, x; + struct stat buf; + regex_t re; + regmatch_t *match; + + asprintf(&msg, "usb_modeswitch called with --symlink-name\n parameter: %s\n", path); + modeswitch_log("%s\n", msg); + + /* In case the device path is returned as /class/tty/ttyUSB, + * get the USB device path from linked tree "device" + */ + asprintf(&linkpath, "/sys%s", path); + asprintf(&dev_linkpath, "/sys%s/device", path); + x = lstat (linkpath, &buf); + if (x == 0) { + if (S_ISLNK (buf.st_mode)) { + n_chars = readlink (linkpath, rawpath_buf, PATH_MAX); + rawpath_buf[n_chars] = '\0'; + n_chars = readlink (dev_linkpath, dev_rawpath_buf, PATH_MAX); + dev_rawpath_buf[n_chars] = '\0'; + if (n_chars > 0) { + trimpath = basename ((char*)dev_rawpath_buf); + } + } + } + else + perror("Couldn't read path for symlink name"); + + cleanpath = join_path((char*)rawpath_buf, (char*)dev_rawpath_buf); + modeswitch_log("\n Use path %s\n", cleanpath); + + if (regcomp(&re, "ttyUSB[0-9]+", REG_EXTENDED) != 0) { + return NULL; + } + if (regexec(&re, (char*)cleanpath, 0, NULL, 0) != 0) { + modeswitch_log("Could not find port name in path\n %s. Abort", cleanpath); + return NULL; + } + regfree(&re); + + device = trimpath; + modeswitch_log("My name is %s\n", trimpath); + + //TODO: strip the doubledots off rawpath + + if (regcomp(&re, "usb[0-9]+/([0-9]+-[0-9]+)/", REG_EXTENDED) != 0) { + return NULL; + } + match = malloc(sizeof(*match)*2); + if (regexec(&re, cleanpath, 2, match, 0) == 0) { + dev_top = substring(cleanpath, + match[1].rm_so, + match[1].rm_eo - match[1].rm_so); + } + else { + modeswitch_log("Could not find device directory in path\n %s. Aborting", cleanpath); + return NULL; + } + regfree(&re); + free(match); + + match = malloc(sizeof(*match)*2); + sprintf(re_str, "[0-9]+\\.([0-9]+)/%s", trimpath); + if (regcomp(&re, re_str, REG_EXTENDED) != 0) { + return NULL; + } + if (regexec(&re, cleanpath, 2, match, 0) == 0) { + my_if = atoi(substring(cleanpath, + match[1].rm_so, + match[1].rm_eo - match[1].rm_so)); + } + else { + modeswitch_log("Could not find interface number in path\n %s. Abort", cleanpath); + return NULL; + } + regfree(&re); + free(match); + + match = malloc(sizeof(*match)*2); + sprintf(re_str, "^.*(%s[:/])[0-9]", dev_top); + if (regcomp(&re, re_str, REG_EXTENDED) != 0) { + return NULL; + } + + if (regexec(&re, cleanpath, 2, match, 0) == 0) { + if_root = substring(cleanpath, + match[0].rm_so, + match[0].rm_eo - match[0].rm_so); + } + else { + modeswitch_log("Could not find interface root number in path\n %s. Aborting", cleanpath); + return NULL; + } + regfree(&re); + free(match); + + modeswitch_log("\n"); + + dev_dir = malloc(PATH_MAX); + memset(dev_dir, '\0', 1); + token = strtok(strdup(cleanpath), "/"); + while (token != NULL) { + if (strcmp(token, dev_top) == 0) { + seen = 1; + } + w_token = strdup(token); + token = strtok(NULL, "/"); + if (seen == 0) { + strcat(dev_dir, "/"); + strcat(dev_dir, w_token); + } + } + + modeswitch_log("My port is %s, my interface is %d\n", trimpath, my_if); + modeswitch_log(" devDir: %s\n dev_top: %s\n sysPath: %s\n", dev_dir, dev_top, cleanpath); + modeswitch_log(" ifRoot: %s\n", if_root); + + asprintf(&if_dir, "/sys%s.%d", if_root, my_if); + + modeswitch_log("\nCheck my endpoints in %s", if_dir); + if (has_interrupt(if_dir) == 1) { + modeswitch_log("\n--> I am an interrupt port\n"); + right_port = 1; + } + else { + modeswitch_log("\n--> I am not an interrupt port\n"); + right_port = 0; + } + + + /* There are devices with more than one interrupt interface. + * Assume that the lowest of these is usable. Check all + * possible lower interfaces. + */ + if ( right_port == 1 && my_if > 0 ) { + modeswitch_log ("Look for lower ports with interrupt endpoints"); + for (i = 0; i < my_if; i++) { + sprintf(if_dir, "/sys%s.%d", if_root, i); + modeswitch_log (" in ifDir %s ...", if_dir); + if (has_interrupt (if_dir) == 1) { + modeswitch_log ("\n--> found an interrupt interface below me\n"); + right_port = 0; + break; + } + } + } + + if (right_port == 0) { + modeswitch_log ("Return empty name and exit\n"); + return strdup(""); + } + + modeswitch_log ("\n--> No interrupt interface below me\n"); + + idx = 2; + asprintf (&symlink_basename, "gsmmodem"); + while ( idx < 256 ) { + asprintf(&symlink_name, "%s%d", symlink_basename, idx); + x = lstat (symlink_name, &buf); + if (x != 0) + break; + free(symlink_name); + symlink_name = NULL; + idx++; + } + free (symlink_basename); + + if (symlink_name != NULL) { + modeswitch_log ("Return symlink name \"%s\" and exit\n", symlink_name); + return symlink_name; + } + else { + return NULL; + } +} + +/* + * Load and bind driver (default "option") + */ +void check_driver_bind (char* vid, char* pid) +{ + const char *path_raw; + char *path = NULL; + char *token = NULL; + char *loader = NULL, *id_file; + char *vendor_spec, *product_spec; + struct stat buf; + int i, x, status; + pipeline *p; + FILE *new_id_f; + + path_raw = getenv("PATH"); + if (path_raw) { + path = strdup(path_raw); + token = strtok(path, ":"); + while (token != NULL) { + asprintf(&loader, "%s/%s", token, MODPROBE); + x = lstat (loader, &buf); + if (x == 0) + break; + else { + free(loader); + loader = NULL; + } + token = strtok(NULL, ":"); + } + token = NULL; + free(path); + } else { + asprintf(&loader, "%s", MODPROBE); + } + + modeswitch_log("Module loader is %s\n", loader); + + asprintf(&id_file, "%s/new_id", config.driver_id_path); + x = lstat(id_file, &buf); + if (x != 0) { /* if id_file (new_id file for module) doesn't exist... */ + if (loader == NULL) { + modeswitch_log("Can't do anymore without module loader; get \"modtools\"!\n"); + return; + } + + modeswitch_log("\nTry to load module \"%s\"\n", config.driver_module); + + p = pipeline_new_command_args (loader, config.driver_module, NULL); + status = pipeline_run(p); + if (status != 0) { + modeswitch_log(" Running \"%s %s\" gave an error: %d\n", loader, config.driver_module, status); + perror(""); + } + else { + modeswitch_log(" Module was loaded successfully: %d\n", status); + } + } + else { + modeswitch_log("Module is active already\n"); + } + + i = 0; + while (i < 50) { + x = lstat(id_file, &buf); + if (x == 0) + break; + sleep(20); + i++; + } + + if (i < 50) { + modeswitch_log("Try to add ID to driver \"%s\"\n", config.driver_module); + syslog(LOG_NOTICE, "usb_modeswitch: add device ID %s:%s to driver %s", vid, pid, config.driver_module); + syslog(LOG_NOTICE, "usb_modeswitch: please report the device ID to the Linux USB developers!"); + new_id_f = fopen(id_file, "a"); + if (new_id_f != NULL) { + if (fprintf(new_id_f, "%s %s ff", vid, pid) > 0) { + modeswitch_log(" ID added to driver; check for new devices in /dev\n"); + } + else { + modeswitch_log("Error adding ID to driver\n"); + } + } + else { + modeswitch_log("Couldn't open ID file for writing\n"); + } + } + else { + modeswitch_log(" \"%s\" not found, check if kernel version is at least 2.6.27\n", + id_file); + modeswitch_log("Fall back to \"usbserial\""); + asprintf(&config.driver_module, "usbserial"); + modeswitch_log("\nTry to unload driver \"%s\"\n", config.driver_module); + p = pipeline_new_command_args (loader, "-r", config.driver_module, NULL); + status = pipeline_run (p); + if (status != 0) { + modeswitch_log(" Running \"%s %s\" gave an error: %d\n", loader, config.driver_module, status); + modeswitch_log("No more fallbacks.\n"); + return; + } + + sleep(50); + + modeswitch_log("\nTry to load driver \"usbserial\" with device IDs\n"); + asprintf(&vendor_spec, "vendor=0x%s", vid); + asprintf(&product_spec, "product=0x%s", pid); + p = pipeline_new_command_args (loader, "-v usbserial", vendor_spec, product_spec, NULL); + free(vendor_spec); + free(product_spec); + status = pipeline_run (p); + if (status != 0) { + modeswitch_log(" Running \"%s usbserial\" gave an error:\n %d\n", loader, status); + } + else { + modeswitch_log(" Driver was loaded successfully:\n%d\n", status); + } + } + + free (loader); + + return; +} + +int parse_device_config (char *device_config) +{ + char *temp_val = NULL; + char line[PATH_MAX]; + FILE* rc; + regex_t module_re, path_re, wb_re, class_re, config_re, success_re, vendor_re, product_re; + regex_t product_list_re, driver_loading_re, mbim_check_re; + regmatch_t *match_m = NULL; + + config.driver_module = 0; + config.driver_id_path = 0; + config.wait_before = 0; + config.target_class = 0; + config.target_vendor = 0; + config.target_product = 0; + config.target_config = -1; /* set as default a value that bConfigurationValue should never have */ + config.check_success = 20; + config.driver_loading = 1; + config.no_mbim_check = 0; + + rc = fopen(device_config, "r"); + + regcomp(&module_re, "^DriverModule[[:space:]]*=[[:space:]]*[[:punct:]]?([[:alnum:]_]*)[[:punct:]]?", REG_EXTENDED); + regcomp(&path_re, "^DriverIDPath[[:space:]]*=[[:space:]]*[[:punct:]]?([[:alnum:]_/:-]+)[[:punct:]]?", REG_EXTENDED); + regcomp(&wb_re, "^WaitBefore[[:space:]]*=[[:space:]]*([0-9]+)", REG_EXTENDED); + regcomp(&vendor_re, "^TargetVendor[[:space:]]*=[[:space:]]*0x([[:alnum:]]+)[[:space:]]?$", REG_EXTENDED); + regcomp(&product_re, "^TargetProduct[[:space:]]*=[[:space:]]*0x([[:alnum:]]+)[[:space:]]?$", REG_EXTENDED); + regcomp(&product_list_re, "^TargetProductList[[:space:]]*=[[:space:]]*[[:punct:]]?([0-9a-fA-F,]+)[[:punct:]]?[[:space:]]?$", REG_EXTENDED); + regcomp(&class_re, "^TargetClass[[:space:]]*=[[:space:]]*0x([[:alnum:]]+)[[:space:]]?$", REG_EXTENDED); + regcomp(&config_re, "^Configuration[[:space:]]*=[[:space:]]*([0-9]+)", REG_EXTENDED); + regcomp(&success_re, "^CheckSuccess[[:space:]]*=[[:space:]]*([0-9]+)", REG_EXTENDED); + regcomp(&driver_loading_re, "^NoDriverLoading[[:space:]]*=[[:space:]]*(1|yes|true)[[:space:]]?$", REG_EXTENDED); + regcomp(&mbim_check_re, "^NoMBIMCheck[[:space:]]*=[[:space:]]*([0-9]+)", REG_EXTENDED); + + while (fgets(line, PATH_MAX, rc) != NULL) { + match_m = malloc(sizeof(*match_m)*2); + if (regexec(&module_re, line, 2, match_m, 0) == 0) { + temp_val = substring(line, + match_m[1].rm_so, + match_m[1].rm_eo - match_m[1].rm_so); + asprintf(&config.driver_module, "%s", temp_val); + modeswitch_log("config: DriverModule set to %s\n", config.driver_module); + } + else if (regexec(&path_re, line, 2, match_m, 0) == 0) { + temp_val = substring(line, + match_m[1].rm_so, + match_m[1].rm_eo - match_m[1].rm_so); + asprintf(&config.driver_id_path, "%s", temp_val); + modeswitch_log("config: DriverIDPath set to %s\n", config.driver_id_path); + } + else if (regexec(&class_re, line, 2, match_m, 0) == 0) { + temp_val = substring(line, + match_m[1].rm_so, + match_m[1].rm_eo - match_m[1].rm_so); + asprintf(&config.target_class, "%s", temp_val); + modeswitch_log("config: TargetClass set to %s\n", config.target_class); + } + else if (regexec(&vendor_re, line, 2, match_m, 0) == 0) { + temp_val = substring(line, + match_m[1].rm_so, + match_m[1].rm_eo - match_m[1].rm_so); + asprintf(&config.target_vendor, "%s", temp_val); + modeswitch_log("config: TargetVendor set to %s\n", config.target_vendor); + } + else if (regexec(&product_re, line, 2, match_m, 0) == 0 || + regexec(&product_list_re, line, 2, match_m, 0) == 0) { + temp_val = substring(line, + match_m[1].rm_so, + match_m[1].rm_eo - match_m[1].rm_so); + asprintf(&config.target_product, "%s", temp_val); + modeswitch_log("config: TargetProduct set to %s\n", config.target_product); + } + else if (regexec(&wb_re, line, 2, match_m, 0) == 0) { + temp_val = substring(line, + match_m[1].rm_so, + match_m[1].rm_eo - match_m[1].rm_so); + config.wait_before = atoi(temp_val); + modeswitch_log("config: WaitBefore set to %d\n", config.wait_before); + } + else if (regexec(&config_re, line, 2, match_m, 0) == 0) { + temp_val = substring(line, + match_m[1].rm_so, + match_m[1].rm_eo - match_m[1].rm_so); + config.target_config = atoi(temp_val); + modeswitch_log("config: Configuration set to %d\n", config.target_config); + } + else if (regexec(&success_re, line, 2, match_m, 0) == 0) { + temp_val = substring(line, + match_m[1].rm_so, + match_m[1].rm_eo - match_m[1].rm_so); + config.check_success = atoi(temp_val); + modeswitch_log("config: CheckSuccess set to %d\n", config.check_success); + } + else if (regexec(&mbim_check_re, line, 2, match_m, 0) == 0) { + temp_val = substring(line, + match_m[1].rm_so, + match_m[1].rm_eo - match_m[1].rm_so); + config.no_mbim_check = atoi(temp_val); + modeswitch_log("config: NoMBIMCheck set to %d\n", config.no_mbim_check); + } + else if (regexec(&driver_loading_re, line, 2, match_m, 0) == 0) { + config.driver_loading = 0; + modeswitch_log("config: NoDriverLoading is set to active\n"); + } + free(match_m); + } + + fclose(rc); + + if (temp_val != NULL) + free(temp_val); + + regfree(&module_re); + regfree(&path_re); + regfree(&wb_re); + regfree(&vendor_re); + regfree(&product_re); + regfree(&product_list_re); + regfree(&class_re); + regfree(&config_re); + regfree(&success_re); + regfree(&driver_loading_re); + + /* + * For general driver loading; TODO: add respective device names. + * Presently only useful for HSO devices (which are recounted now). + */ + if (config.driver_loading) { + if (!config.driver_module || strlen(config.driver_module) == 0) { + asprintf(&config.driver_module, "option"); + asprintf(&config.driver_id_path, "/sys/bus/usb-serial/drivers/option1"); + } + else { + if (!config.driver_id_path || strlen(config.driver_id_path) == 0) + asprintf(&config.driver_id_path, "/sys/bus/usb/drivers/%s", config.driver_module); + } + modeswitch_log("Driver module is \"%s\", ID path is %s\n", config.driver_module, config.driver_id_path); + } + else + modeswitch_log("Driver will not be handled by usb_modeswitch\n"); + + return 1; +} + +void remove_from_bind_list (char *id) +{ + FILE *bind_list_f; + char line[PATH_MAX]; + char *text; + struct list_entry *bind_id, *entry, *copy; + + bind_list_f = fopen (BIND_LIST, "r"); + + if (bind_list_f != NULL) { + bind_id = malloc (sizeof(*bind_id)); + entry = bind_id; + + if (fgets(line, PATH_MAX, bind_list_f) != NULL) { + text = rtrim(line); + if (strcmp(text, "") != 0) + entry->data = strdup(text); + else + entry->data = NULL; + } + else { + free (bind_id); + bind_id = NULL; + } + + while(fgets(line, PATH_MAX, bind_list_f) != NULL) { + text = rtrim(line); + if (entry->data != NULL) { + entry->next = malloc (sizeof(*entry)); + entry = entry->next; + } + if (strcmp(text, "") != 0) + entry->data = strdup(text); + else + entry->data = NULL; + } + fclose (bind_list_f); + + entry->next = NULL; + } + else { + return; + } + + copy = entry = bind_id; + copy = entry == NULL ? NULL : entry->next; + + if (entry != NULL && strcmp(entry->data, id) == 0) { + bind_id = copy; + entry = bind_id; + } + + while (entry != NULL && entry->data != NULL && copy != NULL) { + if (strcmp(copy->data, id) == 0) { + entry->next = copy->next; + } + if (copy->next) { + copy = copy->next; + entry = entry->next; + } + else + break; + } + + if (bind_id == NULL || bind_id->data == NULL) { + if (unlink(BIND_LIST) != 0) { + perror("Couldn't remove empty bind list file"); + } + return; + } + + if ((bind_list_f = fopen (BIND_LIST, "w")) != NULL) { + + entry = bind_id; + while (entry != NULL && entry->data != NULL) { + fprintf(bind_list_f, "%s\n", entry->data); + entry = entry->next; + } + fclose(bind_list_f); + } + else + perror("Couldn't open bind list for writing"); + + free_list(bind_id); + return; +} + +int add_to_list (char* name, char *id) +{ + char *list_file, *line; + char buffer[PATH_MAX]; + struct list_entry *bind_id, *entry; + struct stat buf; + FILE *rc = NULL; + int ret_val; + + ret_val = 0; + + prepare_run_dir(); + + asprintf(&list_file, "%s/%s", RUN_DIR, name); + + bind_id = malloc (sizeof(*bind_id)); + bind_id->data = NULL; + bind_id->next = NULL; + + if (strcmp(name, "bind_list") == 0 && + stat(BIND_LIST, &buf) == 0 && + stat(list_file, &buf) == 0) { + if (rename(BIND_LIST, list_file) == -1) { + perror ("Error renaming the old bind list file"); + ret_val = -1; + goto out; + } + } + + entry = bind_id; + if (stat(list_file, &buf) == 0) { + rc = fopen (list_file, "r"); + while (fgets(buffer, PATH_MAX, rc) != NULL) { + entry->data = NULL; + entry->next = NULL; + line = strdup(rtrim(buffer)); + if (strcmp(line, "") == 0) + continue; + if (strcmp(line, id) == 0) { + // go to out, for cleanup and to return. + ret_val = -1; + goto out; + } + entry->data = line; + entry->next = malloc (sizeof(*entry)); + entry = entry->next; + } + fclose(rc); + rc = NULL; + } + entry->data = strdup(id); + entry->next = NULL; + + if ((rc = fopen (list_file, "w")) != NULL) { + entry = bind_id; + while(entry != NULL && entry->data != NULL) { + fprintf(rc, "%s\n", entry->data); + entry = entry->next; + } + ret_val = 1; + fclose(rc); + rc = NULL; + } + else + perror("Couldn't open bind list for writing"); + +out: + if (rc) + fclose(rc); + free_list(bind_id); + free(list_file); + return ret_val; +} + +int in_bind_list (char *id) +{ + FILE *bind_list; + char buffer[PATH_MAX]; + char *line; + int ret_val; + + if((bind_list = fopen(BIND_LIST, "r")) == NULL) { + perror("Unable to open bind list file"); + return 0; + } + + ret_val = 0; + while (fgets(buffer, PATH_MAX, bind_list) != NULL) { + line = strdup(rtrim(buffer)); + if (strcmp(line, "") == 0) + continue; + if (strcmp(line, id) == 0) { + ret_val = 1; + goto out; + } + } + +out: + fclose(bind_list); + + if (ret_val) + modeswitch_log("Found %s in bind_list\n", id); + else + modeswitch_log("No %s in bind_list\n", id); + + return ret_val; +} + +void modeswitch_log(const char* format, ...) +{ + char *logfile_name; + time_t now; + va_list args; + + if (logging == 0) + return; + + if (logfile == NULL) { + //sprintf(logfile_name, "%s%s", LOGFILE_TEMPL, device); + /* short-circuit temporarily to write to a single log file. */ + asprintf(&logfile_name, "%s", LOGFILE_TEMPL); + if ((logfile = fopen(logfile_name, "w")) != NULL) { + time(&now); + fprintf(logfile, "\n\nUSB_ModeSwitch log from %s\n", ctime(&now)); + } + else { + logging = 0; + perror(logfile_name); + return; + } + } + + if (logfile != NULL) { + va_start(args, format); + vfprintf(logfile, format, args); + va_end(args); + fflush(logfile); + } + + return; +} + +void read_attrs(char *subsystem, struct dev_attr **dev_type, char **attr_list, char *dir) +{ + FILE *rc; + char value[PATH_MAX]; + char *attr_path; + int i; + + if (access(dir, X_OK) == 0) + modeswitch_log("\n%s dir exists: %s\n", subsystem, dir); + + for (i = 0; attr_list[i] != NULL; i++) { + asprintf(&attr_path, "%s/%s", dir, attr_list[i]); + dev_type[i] = malloc(sizeof(**dev_type)); + dev_type[i]->attr = attr_list[i]; + if ((rc = fopen(attr_path, "r")) != NULL) { + if (fgets(value, PATH_MAX, rc) != NULL) { + dev_type[i]->value = strdup(rtrim(value)); + } + else { + dev_type[i]->value = 0; + modeswitch_log("Warning: %s attribute %s not found.\n", subsystem, attr_list[i]); + } + fclose(rc); + } + else { + dev_type[i]->value = 0; + modeswitch_log("Warning: %s attribute \"%s\" not readable.\n", subsystem, attr_list[i]); + perror("Could not read attribute"); + } + free(attr_path); + } + + return; +} + +void read_scsi_attrs(char *dir) +{ + read_attrs("SCSI", scsi, (char **) scsi_attrs, dir); + + return; +} + +void read_usb_attrs(char* dir, char* ifdir) +{ + FILE *rc; + char *bInterfaceClass_fname; + char value[PATH_MAX]; + + read_attrs("USB", usb, (char **) usb_attrs, dir); + + if (ifdir != NULL) { + asprintf(&bInterfaceClass_fname, "%s/bInterfaceClass", ifdir); + if ((rc = fopen(bInterfaceClass_fname, "r")) != NULL) { + if (fgets(value, PATH_MAX, rc) != NULL) + device_iface_class = strdup(value); + } + } + + return; +} + +void config_get_list(char *config_name) +{ + char **config_list; + pipeline *p; + const char *line; + char *config_path,*dup_line; + glob_t config_glob; + int i, j, glob_status; + + // TODO: factor out to main or parse_global_config + if (config.config_pack_path == NULL) { + asprintf(&config.config_pack_path, "%s/%s", DB_DIR, CONFIG_PACK_NAME); + } + // ----------------------- + + /* + * XXX: not quite the same functionality as the original, but it will do for now: + * until there's a better way, append to the list and make sure the last entries + * override previous ones when comes the time to read files. This way, we can read + * the list from the tarball, override with files outside of it if some are shipped + * (or if the tarball doesn't exist), and retain the possibility of overriding any + * such config file with user-written configs from /etc/usb_modeswitch.d + */ + config_list = NULL; + + i = 0; + config_list = realloc(config_list, sizeof(*config_list) * (i + 1) + sizeof(NULL)); + if (access(config.config_pack_path, F_OK) == 0) { + modeswitch_log("Found packed config collection %s\n", config.config_pack_path); + + p = pipeline_new_command_args ("tar", "-tzf", config.config_pack_path, NULL); + pipeline_want_out (p, -1); + pipeline_start (p); + line = pipeline_readline(p); + dup_line = strdup(line); + if (line != NULL) { + config_list[0] = strdup(rtrim(dup_line)); + i++; + } + free(dup_line); + + while ((line = pipeline_readline(p)) != NULL) { + dup_line = strdup(line); + config_list = realloc(config_list, sizeof(*config_list) * (i + 1) + sizeof(NULL)); + config_list[i] = strdup(rtrim(dup_line)); + free(dup_line); + i++; + } + pipeline_free(p); + } + + // Fallback to looking for single files in the db directories + // ... or use extra files to override shipped configurations. + asprintf(&config_path, "%s/%s*", DB_DIR, config_name); + modeswitch_log("Searching entries named: %s\n", config_path); + + glob_status = glob(config_path, 0, NULL, &config_glob); + if (glob_status == 0) { + for (j = 0; config_glob.gl_pathv[j] != NULL; j++, i++) { + config_list = realloc(config_list, sizeof(*config_list) * (i + 2) + sizeof(NULL)); + config_list[i] = strdup(basename(config_glob.gl_pathv[j])); + } + globfree(&config_glob); + } + else if (glob_status == GLOB_NOMATCH) { + globfree(&config_glob); + // try again, with the /etc/ directory now. + sprintf(config_path, "%s/%s*", DB_ETCDIR, config_name); + modeswitch_log("Searching overriding entries named: %s\n", config_path); + + glob_status = glob(config_path, 0, NULL, &config_glob); + if (glob_status == 0) { + for (j = 0; config_glob.gl_pathv[j] != NULL; j++, i++) { + config_list = realloc(config_list, sizeof(*config_list) * (i + 2) + sizeof(NULL)); + config_list[i] = strdup(basename(config_glob.gl_pathv[j])); + } + globfree(&config_glob); + } + else + globfree(&config_glob); + } + else { + modeswitch_log("Error: glob error\n"); + } + config_list[i] = NULL; + + config.config_list = config_list; + + free(config_path); + + return; +} + +char* config_get_config(char *config_name) +{ + FILE *tmpconf; + char *config_path, *etc_config_path; + const char *line; + pipeline *p; + + asprintf(&config_path, "%s/%s", DB_DIR, config_name); + asprintf(&etc_config_path, "%s/%s", DB_ETCDIR, config_name); + + prepare_run_dir (); + + // Overriding from /etc/ or flat files in /usr/share + if (access(etc_config_path, F_OK) == 0) { + modeswitch_log("Use overriden config %s from collection %s\n", config_name, DB_ETCDIR); + syslog(LOG_NOTICE, + "usb_modeswitch: use overriden config %s from collection %s; make sure this is intended", + config_name, DB_ETCDIR); + syslog(LOG_NOTICE, + "usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files"); + config.device_config = strdup(etc_config_path); + } + else if (access(config_path, F_OK) == 0) { + modeswitch_log("Use overriden config %s from collection %s\n", config_name, DB_DIR); + config.device_config = strdup(config_path); + } + else if (access(config.config_pack_path, F_OK) == 0) { + config.device_config = strdup(DEFAULT_TMPCONFIG); + modeswitch_log("Extract config %s from collection %s\n", config_name, config.config_pack_path); + tmpconf = fopen(config.device_config, "w"); + if (!tmpconf) { + perror("Could not open temporary config file for writing"); + free(config.device_config); + config.device_config = NULL; + goto out; + } + p = pipeline_new_command_args ("tar", "-xzOf", config.config_pack_path, config_name, NULL); + pipeline_want_out (p, -1); + pipeline_start (p); + while ((line = pipeline_readline(p)) != NULL) { + fprintf(tmpconf, "%s", line); + } + pipeline_free(p); + fclose(tmpconf); + } + else { + modeswitch_log("Could not determine or load a usable configuration file.\n"); + config.device_config = NULL; + } + +out: + free(config_path); + free(etc_config_path); + + return config.device_config; +} + +int match_device(char *config_name) +{ + char **info_list; + char *cname, *info; + char *token, *attr, *value; + struct dev_attr **match_tmp, **system = NULL; + int i, j, idx, ret_val; + + ret_val = 0; + idx = 0; + + /* skip if config_name is a file left around by a package manager */ + if (strstr(config_name, ".dpkg") != NULL || + strstr(config_name, ".rpm") != NULL) + return ret_val; + + cname = strdup(config_name); + + info_list = NULL; + match_tmp = NULL; + + token = strtok(cname, ":"); + if (token != NULL) { + info_list = realloc(info_list, sizeof(*info_list)); + info_list[0] = token; + } + + i = 1; + while ((token = strtok(NULL, ":")) != NULL) { + info_list = realloc(info_list, sizeof(*info_list) * (i+2) + sizeof(NULL)); + info_list[i] = token; + i++; + } + info_list[i] = NULL; + + if (info_list[0] == NULL || info_list[1] == NULL) { + ret_val = 0; + goto out; + } + + if (info_list[2] == NULL) { + if (strcmp(info_list[0], usb[idVendor]->value) == 0 && + strcmp(info_list[1], usb[idProduct]->value) == 0) + ret_val = 1; + goto out; + } + + token = NULL; + i = 2; + j = 0; + + while (info_list[i] != NULL) { + info = strdup(info_list[i]); + + if (strcmp(info_list[i], "?") == 0) { + ret_val = 0; + goto out; + } + + match_tmp = realloc(match_tmp, sizeof(*match_tmp) * (j+1) + sizeof(NULL)); + + attr = strdup(strtok(info, "=")); + value = strdup(strtok(NULL, "=")); + match_tmp[j] = malloc(sizeof(**match_tmp)); + match_tmp[j]->attr = attr; + + if (value != NULL) { + match_tmp[j]->value = value; + } + else { + match_tmp[j]->value = strdup(""); + } + + free(info); + i++; + j++; + } + match_tmp[j] = NULL; + + for(i = 0; match_tmp[i] != NULL; i++) { + modeswitch_log("match %s\n", match_tmp[i]->attr); + modeswitch_log(" match string: %s\n", match_tmp[i]->value); + + if (match_tmp[i]->attr[0] == 's') + system = scsi; + else if (match_tmp[i]->attr[0] == 'u') + system = usb; + + if (strcmp(match_tmp[i]->attr, "uMa") == 0) + idx = uMa; + else if (strcmp(match_tmp[i]->attr, "uPr") == 0) + idx = uPr; + else if (strcmp(match_tmp[i]->attr, "uSe") == 0) + idx = uSe; + else if (strcmp(match_tmp[i]->attr, "sVe") == 0) + idx = sVe; + else if (strcmp(match_tmp[i]->attr, "sMo") == 0) + idx = sMo; + else if (strcmp(match_tmp[i]->attr, "sRe") == 0) + idx = sRe; + + modeswitch_log(" device string: %s\n", system[idx]->value); + if (system[idx]->value != NULL) { + if (strstr(system[idx]->value, match_tmp[i]->value) == 0) + break; + } + } + + //printf("match/j: %d/%d\n", match, j); + if (match_tmp[i] == NULL) { + ret_val = 1; + } + +out: + for (i = 0; match_tmp != NULL && match_tmp[i] != NULL; i++) { + free(match_tmp[i]->attr); + free(match_tmp[i]->value); + } + + free(info_list); + free(cname); + + return ret_val; +} + +void prepare_run_dir () +{ + int success = 0; + + success = mkdir (RUN_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (success < 0) { + if (errno != EEXIST) { + modeswitch_log("prepare_run_dir(): could not create %s: %s", + RUN_DIR, + strerror(errno)); + _exit(1); + } + else { + /* RUN_DIR already exists, so just ensure that it is writable */ + if (access(RUN_DIR, W_OK) != 0) { + modeswitch_log("prepare_run_dir(): can't write to %s: %s", + RUN_DIR, + strerror(errno)); + _exit(1); + } + } + } +} + +int check_success (char *dir) +{ + char *ifdir; + int i = 0; + + modeswitch_log("Check success of mode switch for max. %d seconds ...", config.check_success); + + for (i = 0; i <= config.check_success; i++) { + sleep(1); + if (access(RUN_DIR, X_OK) != 0) { + modeswitch_log(" Wait for device file system (%d sec.) ...\n", i); + continue; + } + else + modeswitch_log(" Read attributes ..."); + + ifdir = get_iface_ifdir(0); + if (ifdir == NULL) + continue; + read_usb_attrs(dir, basename(ifdir)); + free(ifdir); + + if (usb[idProduct]->value == NULL || usb[idVendor]->value == NULL) { + modeswitch_log(" Essential attributes are missing, continue wait ...\n"); + continue; + } + + if (config.target_class != NULL && device_iface_class != NULL && + strstr(config.target_class, device_iface_class) == NULL) { + modeswitch_log(" Device class couldn't be matched with expected class. ('%s' != '%s')\n", + config.target_class, device_iface_class); + continue; + } + + if (config.target_config != -1 && usb[bConfigurationValue]->value != NULL && + (atoi(usb[bConfigurationValue]->value) != config.target_config)) { + modeswitch_log(" bConfigurationValue doesn't match the expected value. ('%d' != '%d')\n", + config.target_config, atoi(usb[bConfigurationValue]->value)); + continue; + } + + if (config.target_vendor != NULL && usb[idVendor]->value != NULL && + strstr(config.target_vendor, usb[idVendor]->value) == NULL) { + modeswitch_log(" idVendor doesn't match the expected value. ('%s' != '%s')\n", + config.target_vendor, usb[idVendor]->value); + continue; + } + + if (config.target_product != NULL && usb[idProduct]->value != NULL && + strstr(config.target_product, usb[idProduct]->value) == NULL) { + modeswitch_log(" idProduct doesn't match the expected value. ('%s' != '%s')\n", + config.target_product, usb[idProduct]->value); + continue; + } + + modeswitch_log (" All attributes matched\n"); + break; + } + + if (i > 20) + return 0; + + return 1; +} + +void +set_storage_delay (int secs) +{ + int delay_param = -1; + char delay_val[100]; + int current_val; + ssize_t read_count, write_count; + char *write_val; + int write_len; + + write_len = asprintf(&write_val, "%d", secs); + modeswitch_log ("Adjust delay for USB storage devices ...\n"); + + delay_param = open(USB_STORAGE_DELAY_USE, O_RDWR); + if (delay_param > -1) { + read_count = read(delay_param, &delay_val, 100); + if (read_count > 0) { + current_val = atoi(delay_val); + if (current_val < secs) { + write_count = write(delay_param, write_val, write_len); + if ((int)write_count == write_len) + modeswitch_log(" Delay set to %d seconds\n"); + else + modeswitch_log(" Error: could not fully write new value\n"); + } else { + modeswitch_log(" Current value is higher or equal to %d. Leave it alone\n"); + } + } + close(delay_param); + } else { + modeswitch_log ("Error: could not access delay_use attribute: %s\n", strerror(errno)); + } +} + +int +check_mbim_available (void) +{ + int kver_fd = -1; + ssize_t read_count; + char kversion[100]; + char *cdc_mbim_module_path; + struct stat buf; + + kver_fd = open(OSRELEASE_FILE, O_RDONLY); + if (kver_fd > -1) { + read_count = read(kver_fd, &kversion, 100); + if (read_count > 0) { + asprintf(&cdc_mbim_module_path, "%s/%s/%s", + MODULES_PATH, + kversion, + CDC_MBIM_DRIVER_PATH); + if (!stat(cdc_mbim_module_path, &buf)) + goto success; + if (!stat(CDC_MBIM_SYS_PATH, &buf)) + goto success; + } + } + + free(cdc_mbim_module_path); + return 0; +success: + free(cdc_mbim_module_path); + return 1; +} + +char * +get_iface_ifdir (int iface) +{ + char *dev_iface_path; + char *ifdir = NULL; + glob_t path_glob; + struct stat buf; + int glob_status = 0; + + asprintf(&dev_iface_path, "%s/*.%d", config.dev_dir, iface); + glob_status = glob(dev_iface_path, 0, NULL, &path_glob); + if (glob_status != 0) + return NULL; + + if (path_glob.gl_pathc > 0) { + stat(path_glob.gl_pathv[0], &buf); + if (S_ISDIR (buf.st_mode)) + ifdir = strdup(path_glob.gl_pathv[0]); + } + + return ifdir; +} + +int +get_iface_ifclass (int iface) +{ + char *ifdir; + char *bifclass_path; + char class_str[10]; + int fd = -1; + int class = 0; + + ifdir = get_iface_ifdir(iface); + if (ifdir == NULL) + return -1; + + asprintf(&bifclass_path, "%s/bInterfaceClass", ifdir); + free(ifdir); + + fd = open(bifclass_path, O_RDONLY); + if (fd == -1) + return -1; + + read(fd, &class_str, 10); + class = atoi(class_str); + close(fd); + + return class; +} + +int +check_iface (int iface) +{ + int validated; + + validated = get_iface_ifclass(iface); + if (validated == 8 || validated == 3) + return validated; + + return -1; +} Index: b/Makefile =================================================================== --- a/Makefile +++ b/Makefile @@ -45,9 +45,12 @@ jim/libjim.a: dispatcher-script: usb_modeswitch.tcl sed 's_!/usr/bin/tclsh_!'"$(TCL)"'_' < usb_modeswitch.tcl > usb_modeswitch_dispatcher -dispatcher-shared: dispatcher.c usb_modeswitch.string +dispatcher-shared-jim: dispatcher.c usb_modeswitch.string $(CC) dispatcher.c $(LDFLAGS) -Ljim -ljim -Ijim -o usb_modeswitch_dispatcher $(CFLAGS) +dispatcher-shared: + $(CC) $(CFLAGS) usb_modeswitch_dispatcher.c -o usb_modeswitch_dispatcher -lpipeline -ludev + dispatcher-static: jim/libjim.a dispatcher.c usb_modeswitch.string $(CC) dispatcher.c $(LDFLAGS) jim/libjim.a -Ijim -o usb_modeswitch_dispatcher $(CFLAGS) debian/patches/redirect_dispatcher_output.patch0000664000000000000000000000206712317603234017275 0ustar From: Mathieu Trudel-Lapierre Subject: Redirect any and all dispatcher output to /dev/null We really don't care about it anyway, and in some weird cases it causes the dispatcher to crash. --- usb_modeswitch.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) Index: b/usb_modeswitch.sh =================================================================== --- a/usb_modeswitch.sh +++ b/usb_modeswitch.sh @@ -68,7 +68,7 @@ case "$1" in if [ "$?" = "1" ]; then . /lib/udev/hotplug.functions wait_for_file /usr/sbin/usb_modeswitch_dispatcher - exec usb_modeswitch_dispatcher $1 $2 2>>/dev/null + exec usb_modeswitch_dispatcher $1 $2 >>/dev/null 2>&1 fi exit 0 ;; @@ -82,7 +82,7 @@ exec 1<&- 2<&- 5<&- 7<&- elif [ -d "/run/systemd/system/" ]; then # Test if systemd is running exec /bin/systemctl --no-block start usb_modeswitch@$1.service else - exec /usr/sbin/usb_modeswitch_dispatcher --switch-mode $1 & + exec /usr/sbin/usb_modeswitch_dispatcher --switch-mode $1 >>/dev/null 2>&1 & fi exit 0 ) & debian/patches/04_use_system_libjim.patch0000664000000000000000000000200512317322502015663 0ustar Description: Use the package-provided jimsh and libjim Author: Didier Raboud Last-Update: 2013-09-17 --- a/Makefile +++ b/Makefile @@ -13,12 +13,8 @@ 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 +HOST_TCL := /usr/bin/jimsh +TCL := /usr/bin/jimsh JIM_CONFIGURE_OPTS = --disable-lineedit \ --with-out-jim-ext="stdlib posix load signal syslog" --prefix=/usr @@ -49,7 +45,7 @@ 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 +dispatcher-shared: 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 debian/patches/series0000664000000000000000000000023612317604531012037 0ustar 03_use_udev_specifics.patch 04_use_system_libjim.patch 05_upstart_systemd_runtime_detection.patch dispatcher-c-rewrite.patch redirect_dispatcher_output.patch