diff -Nru usb-modeswitch-1.2.3+repack0/ChangeLog usb-modeswitch-2.1.0+repack0/ChangeLog --- usb-modeswitch-1.2.3+repack0/ChangeLog 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/ChangeLog 2014-02-08 22:14:11.000000000 +0000 @@ -2,6 +2,54 @@ History of USB_ModeSwitch ========================= +Version 2.1.0, 2014/01/28 + ATTENTION: -I flag meaning reversed, default is to skip SCSI inquiry; + introduction of StandardEject, replacing many MessageContents with the + same function, reducing size of device config files, and always including + the 'Allow Medium Removal' before ejecting (thanks to Lars Melin for + the idea); fix in "bulk_read", removing bogus CSW request (report from + "Sonya@zte") +Version 2.0.1, 2013/09/03 + Fixed stupid string size bug which could lead to memory corruption + (thanks to Leonid Myravjev) +Version 2.0.0, 2013/09/01 + Switched to libusb1.0, with much help from folks of the wl500g project + (http://wl500g.googlecode.com): Vladislav Grishenko, Leonid Lisovskiy, + Roman Samarev, Andrey Tikhomirov; + major code and debug output cleanup; man page corrections and additions + (thanks to Thomas Haller); + experimental systemd and upstart integration, if present + (Explanation: newer udev versions kill all subprocesses, detached or + not. The suggested way to handle longer running processes like the + usb_modeswitch_dispatcher is to add simple services or tasks and start + these by sending signals from the udev rule) + !! Attention, system integrators: a crude install facility is included + in the Makefile to check if "upstart" or "systemd" is active and to + install the matching service file; you may want to adapt it better to + your respective system, possibly adding dependencies/targets to the + services. Note that the udev starter script usb_modeswitch.sh also + checks for the existence of the service files +Version 1.2.7, 2013/08/07 + Two new dedicated control message functions to support Pantech LTE + (thanks to Adam Goode) and Blackberry Q10/Z10 (thanks to Daniel Mende) +Version 1.2.6, 2013/06/02 + Several changes to streamline compiling as part of larger projects + (thanks to Nicolas Carrier), mostly in Makefile; fix compiler warnings + appearing in certain build environments (N. Carrier); new Quanta + procedure (thanks to Andrey Tikhomirov) for Quanta 1K3 LTE; fix for + error with cascaded hubs in dispatcher script (hint from N. Carrier) +Version 1.2.5, 2012/11/09 + Initial support for MBIM devices, use with data package >= 20121109; + checking for these is the automatic default, new parameter NoMBIMCheck + prevents the check per device in case of problems; new global option + to set "delay_use" of usb-storage (as low values may prevent + mode-switching); fix for handling multi-configuration devices (thanks + to Bjørn Mork for advice) +Version 1.2.4, 2012/08/12 + Additional interface checks to prevent sending UFI commands to non- + storage interfaces (prompted by more ambiguous device IDs popping up); + change in SierraMode for handling newer devices which caused an error + abort before; Makefile fix for parallelized make runs Version 1.2.3, 2012/01/28 Fixed two bugs both causing the embedded-jimsh install variant of the dispatcher crash (the "pure-script" install variant was NOT affected); diff -Nru usb-modeswitch-1.2.3+repack0/Makefile usb-modeswitch-2.1.0+repack0/Makefile --- usb-modeswitch-1.2.3+repack0/Makefile 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/Makefile 2014-02-08 22:14:11.000000000 +0000 @@ -1,77 +1,99 @@ PROG = usb_modeswitch -VERS = 1.2.3 -CC = gcc +VERS = 2.1.0 +CC ?= gcc CFLAGS += -Wall -LIBS = -l usb +LIBS = `pkg-config --libs --cflags libusb-1.0` RM = /bin/rm -f OBJS = usb_modeswitch.c PREFIX = $(DESTDIR)/usr ETCDIR = $(DESTDIR)/etc +SYSDIR = $(ETCDIR)/systemd/system +UPSDIR = $(ETCDIR)/init UDEVDIR = $(DESTDIR)/lib/udev SBINDIR = $(PREFIX)/sbin MANDIR = $(PREFIX)/share/man/man1 -VPATH = jimtcl +VPATH = jimtcl +HOST_TCL := $(shell cd jim && ./autosetup/find-tclsh) +ifeq (,$(findstring jimsh0,$(HOST_TCL))) +TCL ?= $(HOST_TCL) +else +TCL ?= /usr/bin/tclsh +endif +JIM_CONFIGURE_OPTS = --disable-lineedit \ + --with-out-jim-ext="stdlib posix load signal syslog" --prefix=/usr + +.PHONY: clean install install-common uninstall \ + script shared static \ + dispatcher-script dispatcher-shared dispatcher-static \ + install-script install-shared install-static -.PHONY: clean install uninstall +all: script -all: $(PROG) +script: $(PROG) dispatcher-script -shared: $(PROG) dispatcher-dynamic +shared: $(PROG) dispatcher-shared static: $(PROG) dispatcher-static -$(PROG): $(OBJS) +$(PROG): $(OBJS) usb_modeswitch.h $(CC) -o $(PROG) $(OBJS) $(CFLAGS) $(LIBS) $(LDFLAGS) -dispatcher-static: dispatcher.c usb_modeswitch.tcl - ./make_static_dispatcher.sh +jim/libjim.so: + cd jim && CFLAGS="$(CFLAGS)" CC="$(CC)" ./configure $(JIM_CONFIGURE_OPTS) --shared + $(MAKE) -C jim lib + +jim/libjim.a: + cd jim && CFLAGS="$(CFLAGS)" CC="$(CC)" ./configure $(JIM_CONFIGURE_OPTS) + $(MAKE) -C jim lib -dispatcher-dynamic: usb_modeswitch_dispatcher +dispatcher-script: usb_modeswitch.tcl + sed 's_!/usr/bin/tclsh_!'"$(TCL)"'_' < usb_modeswitch.tcl > usb_modeswitch_dispatcher -usb_modeswitch_dispatcher: dispatcher.c usb_modeswitch.string - $(CC) $(CFLAGS) dispatcher.c -ljim -o $@ +dispatcher-shared: jim/libjim.so dispatcher.c usb_modeswitch.string + $(CC) dispatcher.c $(LDFLAGS) -Ljim -ljim -Ijim -o usb_modeswitch_dispatcher $(CFLAGS) + +dispatcher-static: jim/libjim.a dispatcher.c usb_modeswitch.string + $(CC) dispatcher.c $(LDFLAGS) jim/libjim.a -Ijim -o usb_modeswitch_dispatcher $(CFLAGS) usb_modeswitch.string: usb_modeswitch.tcl - jimsh make_string.tcl usb_modeswitch.tcl > $@ + $(HOST_TCL) make_string.tcl usb_modeswitch.tcl > $@ clean: $(RM) usb_modeswitch $(RM) usb_modeswitch_dispatcher $(RM) usb_modeswitch.string + $(RM) jim/autosetup/jimsh0 + $(RM) jim/autosetup/jimsh0.c + +distclean: clean + -$(MAKE) -C jim distclean -distclean: +ums-clean: $(RM) usb_modeswitch $(RM) usb_modeswitch_dispatcher $(RM) usb_modeswitch.string - cd jim && $(MAKE) distclean -install-common: - install -D -s --mode=755 usb_modeswitch $(SBINDIR)/usb_modeswitch +# If the systemd folder is present, install the service for starting the dispatcher +# If not, use the dispatcher directly from the udev rule as in previous versions + +install-common: $(PROG) usb_modeswitch_dispatcher + install -D --mode=755 usb_modeswitch $(SBINDIR)/usb_modeswitch install -D --mode=755 usb_modeswitch.sh $(UDEVDIR)/usb_modeswitch install -D --mode=644 usb_modeswitch.conf $(ETCDIR)/usb_modeswitch.conf install -D --mode=644 usb_modeswitch.1 $(MANDIR)/usb_modeswitch.1 - install -d $(DESTDIR)/var/lib/usb_modeswitch - -install-script: - @SHELL=`which tclsh 2>/dev/null`; \ - if [ -z $$SHELL ]; then \ - SHELL="`which jimsh 2>/dev/null`"; \ - if [ -z $$SHELL ]; then \ - echo "Warning: no Tcl interpreter found!"; \ - SHELL="/usr/bin/tclsh"; \ - fi \ - fi ; \ - sed 's_!/usr/bin/tclsh_!'"$$SHELL"'_' usb_modeswitch_dispatcher + install -D --mode=644 usb_modeswitch_dispatcher.1 $(MANDIR)/usb_modeswitch_dispatcher.1 install -D --mode=755 usb_modeswitch_dispatcher $(SBINDIR)/usb_modeswitch_dispatcher + install -d $(DESTDIR)/var/lib/usb_modeswitch + test -d $(UPSDIR) -a -e /sbin/initctl && install --mode=644 usb-modeswitch-upstart.conf $(UPSDIR) || test 1 + test -d $(SYSDIR) -a -e /usr/bin/systemctl && install --mode=644 usb_modeswitch@.service $(SYSDIR) || test 1 -install-binary: - install -D -s --mode=755 usb_modeswitch_dispatcher $(SBINDIR)/usb_modeswitch_dispatcher +install: install-script -install: all install-common install-script +install-script: dispatcher-script install-common -install-shared: shared install-common install-binary +install-shared: dispatcher-shared install-common -install-static: static install-common install-binary +install-static: dispatcher-static install-common uninstall: $(RM) $(SBINDIR)/usb_modeswitch @@ -80,3 +102,4 @@ $(RM) $(ETCDIR)/usb_modeswitch.conf $(RM) $(MANDIR)/usb_modeswitch.1 $(RM) -R $(DESTDIR)/var/lib/usb_modeswitch + $(RM) $(SYSDIR)/usb_modeswitch@.service diff -Nru usb-modeswitch-1.2.3+repack0/README usb-modeswitch-2.1.0+repack0/README --- usb-modeswitch-1.2.3+repack0/README 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/README 2014-02-08 22:14:11.000000000 +0000 @@ -10,17 +10,21 @@ USB_ModeSwitch is (surprise!) a small mode switching tool for controlling "flip flop" (multiple mode) USB devices. -Several new USB devices (especially high-speed WAN stuff, based on cell phone -chipsets containing that feature) have their MS Windows drivers onboard; when -plugged in for the first time they act like a flash storage and start -installing the driver from there. +More and more USB devices have their MS Windows drivers onboard; when plugged +in for the first time they act like a flash storage and offer their driver +installation from there. After installation (and on every consecutive plugging) the driver switches the mode internally, the storage device vanishes (in most cases), and a new device -(like an USB modem) shows up. Modem maker "Option" calls that feature "ZeroCD -(TM)" since it eliminates the need for shipping a separate driver carrier. +(like an USB modem) shows up. + +At first this feature appeared on devices with cell phone chipsets, presumably +because some of them were able to change the mode of their USB port anyway +from storage to communication - so why not make use of this in a modem stick? +Modem maker "Option" calls that feature "ZeroCD (TM)" since it eliminates the +need for shipping a separate driver carrier. In the beginning, nothing of this was documented in any form and there was -hardly any Linux support available. +hardly any Linux/Unix support available. On the good side, most of the known devices are working out of the box in all modes with the available Linux modules like "usb-storage" or serial USB drivers. That leaves only the problem of the mode-switching from storage to whatever @@ -29,14 +33,18 @@ Fortunately there are things like human intelligence, USB sniffing programs and "libusb". It is possible to eavesdrop the communication of the MS Windows driver, to isolate the command or action that does the switching, and to replay -the same sequence in Linux. +the same sequence in the Unix system. USB_ModeSwitch makes this process easy to handle by taking the relevant para- meters from a configuration file and doing all the initialization and communi- cation stuff, with heavy help from "libusb". -It is mainly used automatically - via udev events and rules - to do the switch -without any user interaction. But it can also be run as a command line tool, -usually when trying to make unknown devices work. + +In Linux and friends it is intended to be used automatically - via udev events +and rules - and doing the mode switch without any user interaction. +However, the core program should be as portable als libusb itself; it does not +rely on specific Linux features. +It can be run as an interactive command line tool, particularly useful when trying +to manage hitherto unknown devices. We have already collected a wide range of information on how to switch all sorts of devices. If you run into a new one that is unknown yet, don't despair: @@ -52,7 +60,10 @@ uninstall") but not mandatory. The wrapper script location changed in 1.1.0; old files might be orphaned but will not do any harm. -The main prerequisite for installing from source is package "libusb-dev" !! +The main prerequisite for installing from source is the development package for +"libusb". It may be called "libusb-dev" or similar in your distribution. From +usb_modeswitch 2.0.0 it should have an "1.x" in the name to reflect the change +to libusb-1. To install the tool set, unpack and run the install command (see below) in the newly created directory. @@ -79,12 +90,17 @@ # make install-static -These commands will install a small posix shell script, the dispatcher -(wrapper) as script or as binary, a global config file, the core program and -a man page. +Note that the "static"/"shared" targets are NOT referring to the usb_modeswitch +program, only to the dispatcher! +Any one of these commands will install a small posix shell script, the +dispatcher (wrapper) as script or as binary, a global config file, the core +program and a man page. Install the data package as well and you are set. +NOTE: installing over (possibly outdated) Linux distribution packages of this +program and the data collection should not be a problem. + How to use ========== @@ -104,7 +120,9 @@ Run "usb_modeswitch -h" to list the command line parameters. See also the provided man page. -Note: Manual use is mainly intended for testing and analyzing ! +Important note: Manual use is mainly intended for testing and analyzing!! +The program puts no limits on what you can send to your USB device, so I +assume it is possible to screw it up profoundly. Once your new device is switching fine you can add it to the data files to make the process automatic. @@ -157,10 +175,10 @@ Whodunit ======== -Copyright 2007, 2008, 2009, 2010, 2011 Josua Dietze (mail to "usb_admin" - at the domain "draisberghof.de" or write a personal message through the forum - to "Josh") - NO SUPPORT QUESTIONS VIA E-MAIL, use the forum! +Copyright 2007 - 2014 Josua Dietze (mail to "usb_admin" at the domain +"draisberghof.de" or write a personal message through the forum to "Josh") + + !!! NO SUPPORT QUESTIONS VIA E-MAIL, use the forum !!! Major contributions: @@ -180,7 +198,7 @@ Hexstr2bin function borrowed from: Jouni Malinen (http://hostap.epitest.fi/wpa_supplicant, from "common.c") -Code, fixes and ideas from: +Code, fixes and ideas contributed by: Aki Makkonen Denis Sutter Lucas Benedicic @@ -199,12 +217,18 @@ Amit Mendapara Roman S. Samarev Chi-Hang Long + Andrey Tikhomirov + Daniel Mende + Nicholas Carrier + Adam Goode + Leonid Lisovskiy + Vladislav Grishenko Device information contributors are named in the "device_reference.txt" file. JimTcl is currently maintained by Steve Bennett; see README in subfolder for details. It is released under a FreeBSD-style license. - Visit http://jim.berlios.de/ + Visit http://jim.tcl.tk/ @@ -227,4 +251,4 @@ -Last revised: 2011-12-26, Josua Dietze +Last revised: 2014-01-28, Josua Dietze diff -Nru usb-modeswitch-1.2.3+repack0/debian/changelog usb-modeswitch-2.1.0+repack0/debian/changelog --- usb-modeswitch-1.2.3+repack0/debian/changelog 2012-03-07 15:48:44.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/changelog 2014-02-14 09:56:57.000000000 +0000 @@ -1,25 +1,116 @@ -usb-modeswitch (1.2.3+repack0-1ubuntu2) precise; urgency=low +usb-modeswitch (2.1.0+repack0-1~p0) precise; urgency=medium - * 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. + * Rebuild on Ubuntu precise. - -- Mathieu Trudel-Lapierre Thu, 09 Feb 2012 10:43:17 -0500 + -- Didier Raboud Fri, 14 Feb 2014 10:56:40 +0100 + +usb-modeswitch (2.1.0+repack0-1~t0) trusty; urgency=medium + + * Rebuild on Ubuntu trusty. + + -- Didier Raboud Fri, 14 Feb 2014 10:49:37 +0100 + +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-1) unstable; urgency=medium @@ -61,22 +152,6 @@ -- 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: @@ -108,35 +183,6 @@ -- 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: diff -Nru usb-modeswitch-1.2.3+repack0/debian/control usb-modeswitch-2.1.0+repack0/debian/control --- usb-modeswitch-1.2.3+repack0/debian/control 2012-03-05 14:58:11.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/control 2014-02-10 13:30:36.000000000 +0000 @@ -1,10 +1,12 @@ Source: usb-modeswitch Section: comm Priority: extra -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Didier Raboud -Build-Depends: debhelper (>= 8.9.0~), libusb-dev, libpipeline-dev, libudev-dev -Standards-Version: 3.9.2 +Maintainer: Didier Raboud +Build-Depends: debhelper (>= 9), + libusb-1.0-0-dev, + pkg-config, + jimsh, libjim-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 @@ -12,7 +14,7 @@ Package: usb-modeswitch Architecture: any Pre-Depends: ${misc:Pre-Depends} -Depends: ${shlibs:Depends}, ${misc:Depends}, usb-modeswitch-data (>= 20110227-1~) +Depends: ${shlibs:Depends}, ${misc:Depends}, usb-modeswitch-data (>= 20140129) Breaks: usb-modeswitch-data (<< 20100127) Suggests: comgt, wvdial Description: mode switching tool for controlling "flip flop" USB devices diff -Nru usb-modeswitch-1.2.3+repack0/debian/patches/01_dont_enable_logging_by_default.patch usb-modeswitch-2.1.0+repack0/debian/patches/01_dont_enable_logging_by_default.patch --- usb-modeswitch-1.2.3+repack0/debian/patches/01_dont_enable_logging_by_default.patch 2012-03-05 14:58:11.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/patches/01_dont_enable_logging_by_default.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -Description: Don't enable logging by default - By default it should "just work", silently. Logging exists for debugging - purposes, so revert the upstream change introduced in 1.2.3. -Origin: vendor -Author: Didier Raboud -Last-Update: 2012-01-30 ---- a/usb_modeswitch.conf -+++ b/usb_modeswitch.conf -@@ -16,4 +16,4 @@ - # Enable logging (results in a extensive report file in /var/log, named - # "usb_modeswitch_" and probably others - --EnableLogging=1 -+EnableLogging=0 diff -Nru usb-modeswitch-1.2.3+repack0/debian/patches/03_use_udev_specifics.patch usb-modeswitch-2.1.0+repack0/debian/patches/03_use_udev_specifics.patch --- usb-modeswitch-1.2.3+repack0/debian/patches/03_use_udev_specifics.patch 2012-03-05 14:58:11.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/patches/03_use_udev_specifics.patch 2014-02-10 13:30:36.000000000 +0000 @@ -2,10 +2,10 @@ This is inspired from alsa-utils' Author: Didier Raboud Origin: vendor -Last-Update: 2012-01-02 +Last-Update: 2013-09-17 --- a/usb_modeswitch.sh +++ b/usb_modeswitch.sh -@@ -66,24 +66,18 @@ +@@ -66,21 +66,17 @@ --symlink-name) device_in "link_list" $v_id $p_id if [ "$?" = "1" ]; then @@ -21,19 +21,22 @@ esac exec 1<&- 2<&- 5<&- 7<&- ( --count=120 +-count=20 -while [ $count != 0 ]; do - if [ ! -e "/usr/sbin/usb_modeswitch_dispatcher" ]; then - sleep 1 - count=$(($count - 1)) - else -- exec usb_modeswitch_dispatcher --switch-mode $1 $0 & -- exit 0 ++ . /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 -+ . /lib/udev/hotplug.functions -+ wait_for_file /usr/sbin/usb_modeswitch_dispatcher -+ exec usb_modeswitch_dispatcher --switch-mode $1 $0 & -+ exit 0 ) & exit 0 diff -Nru usb-modeswitch-1.2.3+repack0/debian/patches/04_cope_with_repack.patch usb-modeswitch-2.1.0+repack0/debian/patches/04_cope_with_repack.patch --- usb-modeswitch-1.2.3+repack0/debian/patches/04_cope_with_repack.patch 2012-03-05 14:58:11.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/patches/04_cope_with_repack.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ ---- a/Makefile -+++ b/Makefile -@@ -43,7 +43,7 @@ - $(RM) usb_modeswitch - $(RM) usb_modeswitch_dispatcher - $(RM) usb_modeswitch.string -- cd jim && $(MAKE) distclean -+ - cd jim && $(MAKE) distclean - - install-common: - install -D -s --mode=755 usb_modeswitch $(SBINDIR)/usb_modeswitch diff -Nru usb-modeswitch-1.2.3+repack0/debian/patches/04_use_system_libjim.patch usb-modeswitch-2.1.0+repack0/debian/patches/04_use_system_libjim.patch --- usb-modeswitch-1.2.3+repack0/debian/patches/04_use_system_libjim.patch 1970-01-01 00:00:00.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/patches/04_use_system_libjim.patch 2014-02-10 13:30:36.000000000 +0000 @@ -0,0 +1,29 @@ +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 diff -Nru usb-modeswitch-1.2.3+repack0/debian/patches/05_upstart_systemd_runtime_detection.patch usb-modeswitch-2.1.0+repack0/debian/patches/05_upstart_systemd_runtime_detection.patch --- usb-modeswitch-1.2.3+repack0/debian/patches/05_upstart_systemd_runtime_detection.patch 1970-01-01 00:00:00.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/patches/05_upstart_systemd_runtime_detection.patch 2014-02-10 13:30:36.000000000 +0000 @@ -0,0 +1,22 @@ +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 diff -Nru usb-modeswitch-1.2.3+repack0/debian/patches/dispatcher-c-rewrite.patch usb-modeswitch-2.1.0+repack0/debian/patches/dispatcher-c-rewrite.patch --- usb-modeswitch-1.2.3+repack0/debian/patches/dispatcher-c-rewrite.patch 2012-03-06 00:34:31.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/patches/dispatcher-c-rewrite.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,2123 +0,0 @@ -From: Mathieu Trudel-Lapierre -Subject: Rewrite the dispatcher in C to avoid requiring Tcl. -Last-Update: 2011-12-05 - ---- - Makefile | 4 - usb_modeswitch_dispatcher.c | 2094 ++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 2096 insertions(+), 2 deletions(-) - -Index: b/usb_modeswitch_dispatcher.c -=================================================================== ---- /dev/null -+++ b/usb_modeswitch_dispatcher.c -@@ -0,0 +1,2094 @@ -+/* -+ Mode switching tool for controlling flip flop (multiple device) USB gear -+ Version 0.0.123, 2012/02/09 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" -+ -+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; -+ -+ 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; -+ -+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, char *ifdir); -+ -+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; -+ 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) { -+ 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("Error: no config database found in /usr/share or /etc. Exiting.\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("No data from udev. Exiting\n"); -+ return 1; -+ } -+ else -+ modeswitch_log("Raw args from udev: %s\n\n", udev_args); -+ -+ /* -+ * arg 0: the bus id for the device (udev: %b) -+ * arg 1: the "kernel name" for the device (udev: %k) -+ * -+ * Both together give the top directory where the path -+ * to the SCSI attributes can be determined (further down) -+ * Addendum: older kernel/udev version seem to differ in -+ * providing these attributes - or not. So more probing -+ * is needed. -+ */ -+ if ((kernel_name = strchr(udev_args, '/')) != NULL) -+ kernel_name = kernel_name + 1; -+ else -+ kernel_name = ""; -+ -+ strncpy (bus_id, udev_args, strlen(udev_args) - strlen(kernel_name) - 1); -+ bus_id[strlen(bus_id)] = '\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) { -+ strncpy (dev_top, kernel_name, strlen(kernel_name) - strlen(colon)); -+ dev_top[strlen(dev_top)] = '\0'; -+ } -+ } -+ else if (strlen(bus_id) > 0) { -+ strcpy(dev_top, bus_id); -+ } -+ else { -+ modeswitch_log("No device number values given from udev! Exiting.\n"); -+ return 1; -+ } -+ -+ if (!*dev_top) { -+ modeswitch_log("Could not determine top device dir from udev values! Exiting.\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)! Exiting.\n", config.dev_dir); -+ return 1; -+ } -+ modeswitch_log("Using top device dir %s\n", config.dev_dir); -+ asprintf(&ifdir, "%s:1.0", basename(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; -+ } -+ -+ modeswitch_log("----------------\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. Exiting.\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)! Exiting.\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, moving 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(" waiting time set to %d seconds.\n", config.wait_before); -+ sleep(config.wait_before); -+ modeswitch_log(" waiting is over, switching starts now.\n"); -+ } -+ -+ /* Now we are actually switching */ -+ no_driver_loading = 0; -+ found_ok = 0; -+ asprintf(&config.vendor_product_param, "-v %s -p %s", usb[idVendor]->value, usb[idProduct]->value); -+ asprintf(&msg, -+ "%s/usb_modeswitch -I %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, ifdir)) { -+ 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 ("Mode switching may have failed. Exiting\n"); -+ return 1; -+ } -+ } -+ else { -+ if (access(config.dev_dir, X_OK) != 0) { -+ modeswitch_log("Device directory in sysfs is done! Something went wrong, aborting.\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 && 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 checking\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. Aborting.\n"); -+ return 1; -+ } -+ -+ -+ /* wait for any drivers to bind automatically */ -+ sleep(1); -+ -+ modeswitch_log("Now checking 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 checking or binding 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("Checking 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, exiting\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(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; -+ regmatch_t *disable_switching_m = NULL, *enable_logging_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; -+ } -+ -+ disable_switching_m = malloc(sizeof(*disable_switching_m)*2); -+ enable_logging_m = malloc(sizeof(*disable_switching_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); -+ } -+ } -+ fclose (rc); -+ -+ regfree(&disable_switching_re); -+ regfree(&enable_logging_re); -+ free(disable_switching_m); -+ free(enable_logging_m); -+ -+ modeswitch_log("Using 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 Using 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. Aborting", 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. Aborting", 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("\nChecking 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 ("Looking 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) -+{ -+ char *default_loader = MODPROBE; -+ char *loader = NULL, *id_file; -+ char *vendor_spec, *product_spec; -+ struct stat buf; -+ int i, x, status; -+ pipeline *p; -+ FILE *new_id_f; -+ -+ x = lstat (default_loader, &buf); -+ if (x != 0) -+ modeswitch_log (" %s not found", default_loader); -+ else -+ loader = strdup(default_loader); -+ 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("\nTrying 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("Trying to add ID to driver \"%s\"\n", config.driver_module); -+ syslog(LOG_NOTICE, "usb_modeswitch: adding 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("Falling back to \"usbserial\""); -+ asprintf(&config.driver_module, "usbserial"); -+ modeswitch_log("\nTrying 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("\nTrying 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); -+ } -+ } -+ -+ 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; -+ 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; -+ -+ 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); -+ -+ 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(&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("Using overriden config %s from collection %s\n", config_name, DB_ETCDIR); -+ config.device_config = strdup(etc_config_path); -+ } -+ else if (access(config_path, F_OK) == 0) { -+ modeswitch_log("Using 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("Extracting 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("matching %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("Checking 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(" Waiting for device file system (%d sec.) ...\n", i); -+ continue; -+ } -+ else -+ modeswitch_log(" Reading attributes ..."); -+ -+ read_usb_attrs(dir, 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; -+} -Index: b/Makefile -=================================================================== ---- a/Makefile -+++ b/Makefile -@@ -28,8 +28,8 @@ dispatcher-static: dispatcher.c usb_mode - - dispatcher-dynamic: usb_modeswitch_dispatcher - --usb_modeswitch_dispatcher: dispatcher.c usb_modeswitch.string -- $(CC) $(CFLAGS) dispatcher.c -ljim -o $@ -+usb_modeswitch_dispatcher: usb_modeswitch_dispatcher.c -+ $(CC) $(CFLAGS) usb_modeswitch_dispatcher.c -lpipeline -ludev -o $@ - - usb_modeswitch.string: usb_modeswitch.tcl - jimsh make_string.tcl usb_modeswitch.tcl > $@ diff -Nru usb-modeswitch-1.2.3+repack0/debian/patches/redirect_dispatcher_output.patch usb-modeswitch-2.1.0+repack0/debian/patches/redirect_dispatcher_output.patch --- usb-modeswitch-1.2.3+repack0/debian/patches/redirect_dispatcher_output.patch 2012-03-05 17:39:07.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/patches/redirect_dispatcher_output.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -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. - -Index: usb-modeswitch/usb_modeswitch.sh -=================================================================== ---- usb-modeswitch.orig/usb_modeswitch.sh 2012-02-09 01:16:23.704164000 -0500 -+++ usb-modeswitch/usb_modeswitch.sh 2012-02-09 01:19:19.952673430 -0500 -@@ -68,7 +68,7 @@ - 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 - ;; -@@ -77,7 +77,7 @@ - ( - . /lib/udev/hotplug.functions - wait_for_file /usr/sbin/usb_modeswitch_dispatcher -- exec usb_modeswitch_dispatcher --switch-mode $1 $0 & -+ exec usb_modeswitch_dispatcher --switch-mode $1 $0 >>/dev/null 2>&1 & - exit 0 - ) & - exit 0 diff -Nru usb-modeswitch-1.2.3+repack0/debian/patches/series usb-modeswitch-2.1.0+repack0/debian/patches/series --- usb-modeswitch-1.2.3+repack0/debian/patches/series 2012-03-05 14:58:11.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/patches/series 2014-02-10 13:30:36.000000000 +0000 @@ -1,5 +1,3 @@ -01_dont_enable_logging_by_default.patch 03_use_udev_specifics.patch -04_cope_with_repack.patch -dispatcher-c-rewrite.patch -redirect_dispatcher_output.patch +04_use_system_libjim.patch +05_upstart_systemd_runtime_detection.patch diff -Nru usb-modeswitch-1.2.3+repack0/debian/rules usb-modeswitch-2.1.0+repack0/debian/rules --- usb-modeswitch-1.2.3+repack0/debian/rules 2012-03-05 14:58:11.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/rules 2014-02-14 09:49:17.000000000 +0000 @@ -9,3 +9,18 @@ # 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" diff -Nru usb-modeswitch-1.2.3+repack0/debian/usb-modeswitch.install usb-modeswitch-2.1.0+repack0/debian/usb-modeswitch.install --- usb-modeswitch-1.2.3+repack0/debian/usb-modeswitch.install 2012-03-05 14:58:11.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/debian/usb-modeswitch.install 2014-02-10 13:30:36.000000000 +0000 @@ -3,3 +3,5 @@ lib/udev/usb_modeswitch usr/share/man/* etc/usb_modeswitch.conf +usb_modeswitch@.service lib/systemd/system/ +usb-modeswitch-upstart.conf etc/init diff -Nru usb-modeswitch-1.2.3+repack0/dispatcher.c usb-modeswitch-2.1.0+repack0/dispatcher.c --- usb-modeswitch-1.2.3+repack0/dispatcher.c 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/dispatcher.c 2014-02-08 22:14:11.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2012 Josua Dietze, usb_modeswitch version 1.2.3 + * Copyright (c) 2011-2014 Josua Dietze, usb_modeswitch version 2.1.0 * Contains code under * Copyright (c) 2010 Wojciech A. Koszek * All rights reserved. diff -Nru usb-modeswitch-1.2.3+repack0/make_static_dispatcher.sh usb-modeswitch-2.1.0+repack0/make_static_dispatcher.sh --- usb-modeswitch-1.2.3+repack0/make_static_dispatcher.sh 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/make_static_dispatcher.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -#!/bin/sh -# This will customize the jim library for use with usb_modeswitch -# and make the compile result optimized for small size -if [ ! -e "jim/libjim.a" ]; then - cd jim - if [ ! -e "autosetup/jimsh0.c" ]; then - echo "Creating the Jim bootstrap source ..." - ./make-bootstrap-jim >autosetup/jimsh0.c - fi - export CFLAGS="-Os" - echo "Configuring the Jim library ..." - ./configure --disable-lineedit --with-out-jim-ext="stdlib posix load signal syslog" --prefix=/usr - echo "Compiling the Jim library ..." - make lib - cd .. -fi - -SHELL=`which tclsh 2>/dev/null` -if [ -z $SHELL ]; then - SHELL=`which jimsh 2>/dev/null` -fi -if [ -z $SHELL ]; then - SHELL="jim/autosetup/jimsh0" - if [ ! -e $SHELL ] ; then - gcc -o "jim/autosetup/jimsh0" "jim/autosetup/jimsh0.c" - fi - if [ ! -e $SHELL ] ; then - echo "No Tcl shell found!" - exit 1 - fi -else - echo "" - echo "------" - echo "Note: found a Tcl shell on your system; embedded interpreter not essential." - echo "Recommending default installation with \"make install\" ..." - echo "------" - echo "" -fi - -$SHELL make_string.tcl usb_modeswitch.tcl >usb_modeswitch.string - -export CFLAGS="$CFLAGS -Wall -I./jim" -export LDLIBS="$LDLIBS -L./jim -ljim" -echo "Compiling the usb_modeswitch dispatcher ..." -gcc $CFLAGS dispatcher.c $LDLIBS -o usb_modeswitch_dispatcher -strip usb_modeswitch_dispatcher -echo "Done!" - diff -Nru usb-modeswitch-1.2.3+repack0/make_string.tcl usb-modeswitch-2.1.0+repack0/make_string.tcl --- usb-modeswitch-1.2.3+repack0/make_string.tcl 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/make_string.tcl 2014-02-08 22:14:11.000000000 +0000 @@ -2,7 +2,7 @@ # (c) Josua Dietze 2012 # -# Usage: mk_script_string.tcl source.tcl >jim-source.c +# Usage: make_string.tcl source.tcl >jim-source.c # Converts a Tcl source file into C source suitable # for using as an embedded script. diff -Nru usb-modeswitch-1.2.3+repack0/usb-modeswitch-upstart.conf usb-modeswitch-2.1.0+repack0/usb-modeswitch-upstart.conf --- usb-modeswitch-1.2.3+repack0/usb-modeswitch-upstart.conf 1970-01-01 00:00:00.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/usb-modeswitch-upstart.conf 2014-02-08 22:14:11.000000000 +0000 @@ -0,0 +1,5 @@ +start on usb-modeswitch-upstart +task +script + exec /usr/sbin/usb_modeswitch_dispatcher --switch-upstart $UMS_PARAM +end script diff -Nru usb-modeswitch-1.2.3+repack0/usb_modeswitch.1 usb-modeswitch-2.1.0+repack0/usb_modeswitch.1 --- usb-modeswitch-1.2.3+repack0/usb_modeswitch.1 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/usb_modeswitch.1 2014-02-08 22:14:11.000000000 +0000 @@ -3,27 +3,30 @@ usb_modeswitch - switch mode of "multi-state" USB devices .SH "SYNOPSIS" .PP -\fBusb_modeswitch\fR [\fB\-heWQDIvpVPmM23rwdHSOBGTNALnsRiuagft\fP] [\fB\-c \fIfilename\fP] +\fBusb_modeswitch\fR [\fB\-heWQDIvpVPmM23rwKdHSOBGTNALnsRiuagft\fP] [\fB\-c \fIfilename\fP] .SH "DESCRIPTION" .PP Several new USB devices have their proprietary Windows drivers onboard, -most of them WAN dongles. When plugged in for the first time, they act -like a flash storage and start installing the Windows driver from there. -If the driver is already installed, it makes the storage device disappear -and a new device, mainly composite with modem ports, shows up. +most of them WWAN and WLAN dongles. When plugged in for the first time, +they act like a flash storage and start installing the Windows driver from +there. If the driver is already installed, it makes the storage device +disappear and a new device, mainly composite with modem ports, shows up. .PP On Linux, in most cases the drivers are available as kernel modules, -such as "usbserial" or "option". However, the device shows up as -"usb-storage" by default. \fBusb_modeswitch\fR can send a provided bulk -message (most likely a mass storage command) to the device which is known -to initiate the mode switching. +such as "usbserial" or "option". However, the device initially binds to +"usb-storage" by default. \fBusb_modeswitch\fR can then send a provided bulk +message (most likely a mass storage command) to the device; this message +has to be determined by analyzing the actions of the Windows driver. .PP In some cases, USB control commands are used for switching. These cases are handled by custom functions, and no bulk message needs to be provided. .PP Usually, the program is distributed with a set of configurations for many -known devices, which in combination with a wrapper script launched from the -udev daemon allows a fully automatic handling of a device upon insertion. +known devices, which allows a fully automatic handling of a device upon +insertion, made possible by combining usb_modeswitch with the wrapper script +\fBusb_modeswitch_dispatcher\fR which is launched by the udev daemon. +.PP +Note that \fBusb_modeswitch\fR itself has no specific Linux dependencies. .SH "OPTIONS" .PP @@ -45,10 +48,12 @@ Target vendor ID. When given will be searched for and detected initially for information purposes. If success checking (option \-s) is active, providing target IDs (vendor/product) or target class is recommended +.IP "\fB-j\fP \fB\-\-find-mbim\fP " 10 +Return configuration number with MBIM interface and exit. .IP "\fB-P\fP \fB\-\-target-product NUM\fP " 10 Target product ID -.IP "\fB-b\fP \fB\-\-busnum NUM\fP " 10 -.IP "\fB-g\fP \fB\-\-devnum NUM\fP " 10 +.IP "\fB-b\fP \fB\-\-bus-num NUM\fP " 10 +.IP "\fB-g\fP \fB\-\-device-num NUM\fP " 10 If bus and device number are provided, the handling of a specific device on a specific USB port is guaranteed, in contrast to using only the USB ID. This is important if there are multiple similar devices on a system @@ -81,6 +86,10 @@ Try to read the response to a storage command from there if option \-n is active. Only for testing purposes; usually endpoints are determined from the device attributes +.IP "\fB-K\fP \fB\-\-std-eject\fP " 10 +Apply the standard SCSI sequence of "Allow Medium Removal" and +"Eject". Implies \fB-n\fP. One 'Message' can be added with \fB-M\fP +that will be transmitted after the eject sequence .IP "\fB-d\fP \fB\-\-detach-only\fP " 10 Just detach the current driver. This is sufficient for some early devices to switch successfully. Otherwise this feature can @@ -94,14 +103,20 @@ Send a special control message used by GCT chipsets .IP "\fB-T\fP \fB\-\-kobil-mode\fP " 10 Send a special control message used by Kobil devices -.IP "\fB-T\fP \fB\-\-sequans-mode\fP " 10 +.IP "\fB-N\fP \fB\-\-sequans-mode\fP " 10 Send a special control message used by Sequans chipset -.IP "\fB-T\fP \fB\-\-mobileaction-mode\fP " 10 +.IP "\fB-A\fP \fB\-\-mobileaction-mode\fP " 10 Send a special control message used by the MobileAction device .IP "\fB-B\fP \fB\-\-qisda-mode\fP " 10 Send a special control message used by Qisda devices +.IP "\fB-E\fP \fB\-\-quanta-mode\fP " 10 +Send a special control message used by Quanta devices +.IP "\fB-F\fP \fB\-\-pantech-mode\fP " 10 +Send a special control message used by Pantech devices +.IP "\fB-Z\fP \fB\-\-blackberry-mode\fP " 10 +Send a special control message used by some newer Blackberry devices .IP "\fB-O\fP \fB\-\-sony-mode\fP " 10 -Apply a special sequence used by Sony Ericsson devices. Implies option \--success +Apply a special sequence used by Sony Ericsson devices. Implies option \--check-success .IP "\fB-L\fP \fB\-\-cisco-mode\fP " 10 Send a sequence of bulk messages used by Cisco devices .IP "\fB-R\fP \fB\-\-reset-usb\fP " 10 @@ -110,7 +125,7 @@ interactions. Few devices need it to complete the switching; apart from that it may be useful during testing -.IP "\fB-c\fP \fB\-\-config FILENAME\fP " 10 +.IP "\fB-c\fP \fB\-\-config-file FILENAME\fP " 10 Use a specific config file. If any ID or switching options are given as command line parameters, this option is ignored. In that case all mandatory parameters have to be provided on @@ -129,7 +144,7 @@ Changes the behaviour of the program slightly. A success message including the effective target device ID is put out and a syslog notice is issued. Mainly for integration with a wrapper script -.IP "\fB-s\fP \fB\-\-success NUM\fP " 10 +.IP "\fB-s\fP \fB\-\-check-success NUM\fP " 10 After switching, keep checking for the result up to max. NUM seconds. If target IDs or target class were provided, their appearance indicates certain success. Otherwise the disconnection of the original device is rated as likely proof @@ -156,4 +171,4 @@ The complete text of the current GNU General Public License can be found in http://www.gnu.org/licenses/gpl.txt -.\" last edited 2011-10-23 for version 1.2.2 +.\" last edited 2014-01-28 for version 2.1.0 diff -Nru usb-modeswitch-1.2.3+repack0/usb_modeswitch.c usb-modeswitch-2.1.0+repack0/usb_modeswitch.c --- usb-modeswitch-1.2.3+repack0/usb_modeswitch.c 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/usb_modeswitch.c 2014-02-08 22:14:11.000000000 +0000 @@ -1,8 +1,8 @@ /* - Mode switching tool for controlling flip flop (multiple device) USB gear - Version 1.2.3, 2012/01/28 + Mode switching tool for controlling flip flop (multiple mode) USB devices + Version 2.1.0, 2014/01/28 - Copyright (C) 2007 - 2012 Josua Dietze (mail to "usb_admin" at the domain + Copyright (C) 2007 - 2014 Josua Dietze (mail to "usb_admin" at the domain of the home page; or write a personal message through the forum to "Josh". NO SUPPORT VIA E-MAIL - please use the forum for that) @@ -45,7 +45,7 @@ /* Recommended tab size: 4 */ -#define VERSION "1.2.3" +#define VERSION "2.1.0" #include #include @@ -54,10 +54,48 @@ #include #include #include +#include -#include #include "usb_modeswitch.h" + +/* libusb 1.0 wrappers */ + +int usb_bulk_io(struct libusb_device_handle *handle, int ep, char *bytes, + int size, int timeout) +{ + int actual_length; + int r; +// usbi_dbg("endpoint %x size %d timeout %d", ep, size, timeout); + r = libusb_bulk_transfer(handle, ep & 0xff, (unsigned char *)bytes, size, + &actual_length, timeout); + + /* if we timed out but did transfer some data, report as successful short + * read. FIXME: is this how libusb-0.1 works? */ + if (r == 0 || (r == LIBUSB_ERROR_TIMEOUT && actual_length > 0)) + return actual_length; + + return r; +} + +static int usb_interrupt_io(libusb_device_handle *handle, int ep, char *bytes, + int size, int timeout) +{ + int actual_length; + int r; +// usbi_dbg("endpoint %x size %d timeout %d", ep, size, timeout); + r = libusb_interrupt_transfer(handle, ep & 0xff, (unsigned char *)bytes, size, + &actual_length, timeout); + + /* if we timed out but did transfer some data, report as successful short + * read. FIXME: is this how libusb-0.1 works? */ + if (r == 0 || (r == LIBUSB_ERROR_TIMEOUT && actual_length > 0)) + return actual_length; + + return (r); +} + + #define LINE_DIM 1024 #define MAXLINES 50 #define BUF_SIZE 4096 @@ -73,8 +111,9 @@ char *TempPP=NULL; -struct usb_device *dev; -struct usb_dev_handle *devh; +static struct libusb_context *ctx = NULL; +static struct libusb_device *dev; +static struct libusb_device_handle *devh; int DefaultVendor=0, DefaultProduct=0, TargetVendor=0, TargetProduct=-1, TargetClass=0; int MessageEndpoint=0, ResponseEndpoint=0, ReleaseDelay=0; @@ -82,10 +121,24 @@ int devnum=-1, busnum=-1; int ret; -char DetachStorageOnly=0, HuaweiMode=0, SierraMode=0, SonyMode=0, GCTMode=0, KobilMode=0; -char SequansMode=0, MobileActionMode=0, CiscoMode=0, QisdaMode=0; +unsigned int ModeMap = 0; +#define DETACHONLY_MODE 0x00000001 +#define HUAWEI_MODE 0x00000002 +#define SIERRA_MODE 0x00000004 +#define SONY_MODE 0x00000008 +#define GCT_MODE 0x00000010 +#define KOBIL_MODE 0x00000020 +#define SEQUANS_MODE 0x00000040 +#define MOBILEACTION_MODE 0x00000080 +#define CISCO_MODE 0x00000100 +#define QISDA_MODE 0x00000200 +#define QUANTA_MODE 0x00000400 +#define BLACKBERRY_MODE 0x00000800 +#define PANTECH_MODE 0x00001000 + char verbose=0, show_progress=1, ResetUSB=0, CheckSuccess=0, config_read=0; -char NeedResponse=0, NoDriverLoading=0, InquireDevice=1, sysmode=0; +char NeedResponse=0, NoDriverLoading=0, InquireDevice=0, sysmode=0, mbim=0; +char StandardEject=0; char imanufact[DESCR_MAX], iproduct[DESCR_MAX], iserial[DESCR_MAX]; @@ -93,6 +146,7 @@ char MessageContent2[LINE_DIM]; char MessageContent3[LINE_DIM]; char TargetProductList[LINE_DIM]; +char DefaultProductList[5]; char ByteString[LINE_DIM/2]; char buffer[BUF_SIZE]; @@ -124,19 +178,24 @@ {"sierra-mode", no_argument, 0, 'S'}, {"sony-mode", no_argument, 0, 'O'}, {"qisda-mode", no_argument, 0, 'B'}, + {"quanta-mode", no_argument, 0, 'E'}, {"kobil-mode", no_argument, 0, 'T'}, {"gct-mode", no_argument, 0, 'G'}, {"sequans-mode", no_argument, 0, 'N'}, {"mobileaction-mode", no_argument, 0, 'A'}, {"cisco-mode", no_argument, 0, 'L'}, + {"blackberry-mode", no_argument, 0, 'Z'}, + {"pantech-mode", no_argument, 0, 'F'}, + {"std-eject", no_argument, 0, 'K'}, {"need-response", no_argument, 0, 'n'}, {"reset-usb", no_argument, 0, 'R'}, {"config-file", required_argument, 0, 'c'}, {"verbose", no_argument, 0, 'W'}, {"quiet", no_argument, 0, 'Q'}, {"sysmode", no_argument, 0, 'D'}, - {"no-inquire", no_argument, 0, 'I'}, + {"inquire", no_argument, 0, 'I'}, {"stdinput", no_argument, 0, 't'}, + {"find-mbim", no_argument, 0, 'j'}, {"long-config", required_argument, 0, 'f'}, {"check-success", required_argument, 0, 's'}, {"interface", required_argument, 0, 'i'}, @@ -154,16 +213,20 @@ ParseParamHex(configFilename, TargetClass); ParseParamHex(configFilename, DefaultVendor); ParseParamHex(configFilename, DefaultProduct); - ParseParamBool(configFilename, DetachStorageOnly); - ParseParamBool(configFilename, HuaweiMode); - ParseParamBool(configFilename, SierraMode); - ParseParamBool(configFilename, SonyMode); - ParseParamBool(configFilename, QisdaMode); - ParseParamBool(configFilename, GCTMode); - ParseParamBool(configFilename, KobilMode); - ParseParamBool(configFilename, SequansMode); - ParseParamBool(configFilename, MobileActionMode); - ParseParamBool(configFilename, CiscoMode); + ParseParamBoolMap(configFilename, DetachStorageOnly, ModeMap, DETACHONLY_MODE); + ParseParamBoolMap(configFilename, HuaweiMode, ModeMap, HUAWEI_MODE); + ParseParamBoolMap(configFilename, SierraMode, ModeMap, SIERRA_MODE); + ParseParamBoolMap(configFilename, SonyMode, ModeMap, SONY_MODE); + ParseParamBoolMap(configFilename, GCTMode, ModeMap, GCT_MODE); + ParseParamBoolMap(configFilename, KobilMode, ModeMap, KOBIL_MODE); + ParseParamBoolMap(configFilename, SequansMode, ModeMap, SEQUANS_MODE); + ParseParamBoolMap(configFilename, MobileActionMode, ModeMap, MOBILEACTION_MODE); + ParseParamBoolMap(configFilename, CiscoMode, ModeMap, CISCO_MODE); + ParseParamBoolMap(configFilename, QisdaMode, ModeMap, QISDA_MODE); + ParseParamBoolMap(configFilename, QuantaMode, ModeMap, QUANTA_MODE); + ParseParamBoolMap(configFilename, BlackberryMode, ModeMap, BLACKBERRY_MODE); + ParseParamBoolMap(configFilename, PantechMode, ModeMap, PANTECH_MODE); + ParseParamBool(configFilename, StandardEject); ParseParamBool(configFilename, NoDriverLoading); ParseParamHex(configFilename, MessageEndpoint); ParseParamString(configFilename, MessageContent); @@ -192,41 +255,49 @@ void printConfig() { if ( DefaultVendor ) - printf ("DefaultVendor= 0x%04x\n", DefaultVendor); - else - fprintf (output,"DefaultVendor= not set\n"); + fprintf (output,"DefaultVendor= 0x%04x\n", DefaultVendor); if ( DefaultProduct ) fprintf (output,"DefaultProduct= 0x%04x\n", DefaultProduct); - else - fprintf (output,"DefaultProduct= not set\n"); if ( TargetVendor ) fprintf (output,"TargetVendor= 0x%04x\n", TargetVendor); - else - fprintf (output,"TargetVendor= not set\n"); if ( TargetProduct > -1 ) fprintf (output,"TargetProduct= 0x%04x\n", TargetProduct); - else - fprintf (output,"TargetProduct= not set\n"); if ( TargetClass ) fprintf (output,"TargetClass= 0x%02x\n", TargetClass); - else - fprintf (output,"TargetClass= not set\n"); - fprintf (output,"TargetProductList=\"%s\"\n", TargetProductList); - fprintf (output,"\nDetachStorageOnly=%i\n", (int)DetachStorageOnly); - fprintf (output,"HuaweiMode=%i\n", (int)HuaweiMode); - fprintf (output,"SierraMode=%i\n", (int)SierraMode); - fprintf (output,"SonyMode=%i\n", (int)SonyMode); - fprintf (output,"QisdaMode=%i\n", (int)QisdaMode); - fprintf (output,"GCTMode=%i\n", (int)GCTMode); - fprintf (output,"KobilMode=%i\n", (int)KobilMode); - fprintf (output,"SequansMode=%i\n", (int)SequansMode); - fprintf (output,"MobileActionMode=%i\n", (int)MobileActionMode); - fprintf (output,"CiscoMode=%i\n", (int)CiscoMode); + if ( strlen(TargetProductList) ) + fprintf (output,"TargetProductList=\"%s\"\n", TargetProductList); + if (StandardEject) + fprintf (output,"\nStandardEject=1\n"); + if (ModeMap & DETACHONLY_MODE) + fprintf (output,"\nDetachStorageOnly=1\n"); + if (ModeMap & HUAWEI_MODE) + fprintf (output,"HuaweiMode=1\n"); + if (ModeMap & SIERRA_MODE) + fprintf (output,"SierraMode=1\n"); + if (ModeMap & SONY_MODE) + fprintf (output,"SonyMode=1\n"); + if (ModeMap & QISDA_MODE) + fprintf (output,"QisdaMode=1\n"); + if (ModeMap & QUANTA_MODE) + fprintf (output,"QuantaMode=1\n"); + if (ModeMap & GCT_MODE) + fprintf (output,"GCTMode=1\n"); + if (ModeMap & KOBIL_MODE) + fprintf (output,"KobilMode=1\n"); + if (ModeMap & SEQUANS_MODE) + fprintf (output,"SequansMode=1\n"); + if (ModeMap & MOBILEACTION_MODE) + fprintf (output,"MobileActionMode=1\n"); + if (ModeMap & CISCO_MODE) + fprintf (output,"CiscoMode=1\n"); + if (ModeMap & BLACKBERRY_MODE) + fprintf (output,"BlackberryMode=1\n"); + if (ModeMap & PANTECH_MODE) + fprintf (output,"PantechMode=1\n"); if ( MessageEndpoint ) fprintf (output,"MessageEndpoint=0x%02x\n", MessageEndpoint); - else - fprintf (output,"MessageEndpoint= not set\n"); - fprintf (output,"MessageContent=\"%s\"\n", MessageContent); + if ( strlen(MessageContent) ) + fprintf (output,"MessageContent=\"%s\"\n", MessageContent); if ( strlen(MessageContent2) ) fprintf (output,"MessageContent2=\"%s\"\n", MessageContent2); if ( strlen(MessageContent3) ) @@ -234,8 +305,6 @@ fprintf (output,"NeedResponse=%i\n", (int)NeedResponse); if ( ResponseEndpoint ) fprintf (output,"ResponseEndpoint=0x%02x\n", ResponseEndpoint); - else - fprintf (output,"ResponseEndpoint= not set\n"); if ( Interface > -1 ) fprintf (output,"Interface=0x%02x\n", Interface); if ( Configuration > 0 ) @@ -243,18 +312,11 @@ if ( AltSetting > -1 ) fprintf (output,"AltSetting=0x%02x\n", AltSetting); if ( InquireDevice ) - fprintf (output,"\nInquireDevice enabled (default)\n"); - else - fprintf (output,"\nInquireDevice disabled\n"); + fprintf (output,"\nInquireDevice=1\n"); if ( CheckSuccess ) fprintf (output,"Success check enabled, max. wait time %d seconds\n", CheckSuccess); - else - fprintf (output,"Success check disabled\n"); if ( sysmode ) fprintf (output,"System integration mode enabled\n"); - else - fprintf (output,"System integration mode disabled\n"); - fprintf (output,"\n"); } @@ -271,7 +333,7 @@ while (1) { - c = getopt_long (argc, argv, "heWQDndHSOBGTNALRItv:p:V:P:C:m:M:2:3:w:r:c:i:u:a:s:f:b:g:", + c = getopt_long (argc, argv, "hejWQDndKHSOBEGTNALZFRItv:p:V:P:C:m:M:2:3:w:r:c:i:u:a:s:f:b:g:", long_options, &option_index); /* Detect the end of the options. */ @@ -293,29 +355,34 @@ case 'w': ReleaseDelay = strtol(optarg, NULL, 10); break; case 'n': NeedResponse = 1; break; case 'r': ResponseEndpoint = strtol(optarg, NULL, 16); break; - case 'd': DetachStorageOnly = 1; break; - case 'H': HuaweiMode = 1; break; - case 'S': SierraMode = 1; break; - case 'O': SonyMode = 1; break; - case 'B': QisdaMode = 1; break; - case 'G': GCTMode = 1; break; - case 'T': KobilMode = 1; break; - case 'N': SequansMode = 1; break; - case 'A': MobileActionMode = 1; break; - case 'L': CiscoMode = 1; break; + case 'K': StandardEject = 1; break; + case 'd': ModeMap = ModeMap + DETACHONLY_MODE; break; + case 'H': ModeMap = ModeMap + HUAWEI_MODE; break; + case 'S': ModeMap = ModeMap + SIERRA_MODE; break; + case 'O': ModeMap = ModeMap + SONY_MODE; break;; break; + case 'B': ModeMap = ModeMap + QISDA_MODE; break; + case 'E': ModeMap = ModeMap + QUANTA_MODE; break; + case 'G': ModeMap = ModeMap + GCT_MODE; break; + case 'T': ModeMap = ModeMap + KOBIL_MODE; break; + case 'N': ModeMap = ModeMap + SEQUANS_MODE; break; + case 'A': ModeMap = ModeMap + MOBILEACTION_MODE; break; + case 'L': ModeMap = ModeMap + CISCO_MODE; break; + case 'Z': ModeMap = ModeMap + BLACKBERRY_MODE; break; + case 'F': ModeMap = ModeMap + PANTECH_MODE; break; case 'c': readConfigFile(optarg); break; case 't': readConfigFile("stdin"); break; case 'W': verbose = 1; show_progress = 1; count--; break; case 'Q': show_progress = 0; verbose = 0; count--; break; case 'D': sysmode = 1; count--; break; case 's': CheckSuccess = strtol(optarg, NULL, 10); count--; break; - case 'I': InquireDevice = 0; break; + case 'I': InquireDevice = 1; break; case 'b': busnum = strtol(optarg, NULL, 10); break; case 'g': devnum = strtol(optarg, NULL, 10); break; case 'i': Interface = strtol(optarg, NULL, 16); break; case 'u': Configuration = strtol(optarg, NULL, 16); break; case 'a': AltSetting = strtol(optarg, NULL, 16); break; + case 'j': mbim = 1; break; case 'f': longConfig = malloc(strlen(optarg)+5); @@ -349,8 +416,10 @@ int main(int argc, char **argv) { - int numDefaults=0, specialMode=0, sonySuccess=0; + int numDefaults=0, sonySuccess=0; int currentConfig=0, defaultClass=0, interfaceClass=0; + struct libusb_device_descriptor descriptor; + struct libusb_config_descriptor *config; /* Make sure we have empty strings even if not set by config */ @@ -358,6 +427,7 @@ MessageContent[0] = '\0'; MessageContent2[0] = '\0'; MessageContent3[0] = '\0'; + DefaultProductList[0] = '\0'; /* Useful for debugging during boot */ // output=fopen("/dev/console", "w"); @@ -375,31 +445,31 @@ break; default: /* one or more arguments except -W, -q or -s */ if (!config_read) /* if arguments contain -c, the config file was already processed */ - if (verbose) fprintf(output,"Taking all parameters from the command line\n\n"); + if (verbose) fprintf(output,"Take all parameters from the command line\n\n"); } - if (verbose) + if (verbose) { printVersion(); - - if (verbose) printConfig(); + fprintf(output,"\n"); + } - /* Plausibility checks. The default IDs are mandatory */ + /* Some sanity checks. The default IDs are mandatory */ if (!(DefaultVendor && DefaultProduct)) { - SHOW_PROGRESS(output,"No default vendor/product ID given. Aborting.\n\n"); + SHOW_PROGRESS(output,"No default vendor/product ID given. Abort\n\n"); exit(1); } + if (strlen(MessageContent)) { if (strlen(MessageContent) % 2 != 0) { - fprintf(stderr, "Error: MessageContent hex string has uneven length. Aborting.\n\n"); + fprintf(stderr, "Error: MessageContent hex string has uneven length. Abort\n\n"); exit(1); } if ( hexstr2bin(MessageContent, ByteString, strlen(MessageContent)/2) == -1) { - fprintf(stderr, "Error: MessageContent %s\n is not a hex string. Aborting.\n\n", MessageContent); + fprintf(stderr, "Error: MessageContent %s\n is not a hex string. Abort\n\n", MessageContent); exit(1); } } - SHOW_PROGRESS(output,"\n"); if (devnum == -1) { searchMode = SEARCH_DEFAULT; @@ -410,21 +480,28 @@ if (show_progress) if (CheckSuccess && !(TargetVendor || TargetProduct > -1 || TargetProductList[0] != '\0') && !TargetClass) - printf("Note: target parameter missing; success check limited\n"); + fprintf(output,"Note: No target parameter given; success check limited\n"); + + if (TargetProduct > -1 && TargetProductList[0] == '\0') { + sprintf(TargetProductList,"%04x",TargetProduct); + TargetProduct = -1; + } /* libusb initialization */ - usb_init(); + libusb_init(&ctx); if (verbose) - usb_set_debug(15); + libusb_set_debug(ctx, 3); - usb_find_busses(); - usb_find_devices(); + if (mbim) { + printf("%d\n", findMBIMConfig(DefaultVendor, DefaultProduct, searchMode) ); + exit(0); + } /* Count existing target devices, remember for success check */ - if ((TargetVendor || TargetClass) && searchMode != SEARCH_BUSDEV) { - SHOW_PROGRESS(output,"Looking for target devices ...\n"); - search_devices(&targetDeviceCount, TargetVendor, TargetProduct, TargetProductList, TargetClass, 0, SEARCH_TARGET); + if (searchMode != SEARCH_BUSDEV && (TargetVendor || TargetClass)) { + SHOW_PROGRESS(output,"Look for target devices ...\n"); + search_devices(&targetDeviceCount, TargetVendor, TargetProductList, TargetClass, 0, SEARCH_TARGET); if (targetDeviceCount) { SHOW_PROGRESS(output," Found devices in target mode or class (%d)\n", targetDeviceCount); } else @@ -432,47 +509,59 @@ } /* Count default devices, get the last one found */ - SHOW_PROGRESS(output,"Looking for default devices ...\n"); - dev = search_devices(&numDefaults, DefaultVendor, DefaultProduct, "\0", TargetClass, Configuration, searchMode); + SHOW_PROGRESS(output,"Look for default devices ...\n"); + + sprintf(DefaultProductList,"%04x",DefaultProduct); + dev = search_devices(&numDefaults, DefaultVendor, DefaultProductList, TargetClass, Configuration, searchMode); if (numDefaults) { - SHOW_PROGRESS(output," Found device in default mode, class or configuration (%d)\n", numDefaults); + SHOW_PROGRESS(output," Found devices in default mode (%d)\n", numDefaults); } else { - SHOW_PROGRESS(output," No devices in default mode found. Nothing to do. Bye.\n\n"); + SHOW_PROGRESS(output," No devices in default mode found. Nothing to do. Bye!\n\n"); exit(0); } if (dev == NULL) { - SHOW_PROGRESS(output," No bus/device match. Is device connected? Bye.\n\n"); + SHOW_PROGRESS(output," No bus/device match. Is device connected? Abort\n\n"); exit(0); } else { if (devnum == -1) { - devnum = dev->devnum; - busnum = (int)strtol(dev->bus->dirname,NULL,10); - SHOW_PROGRESS(output,"Accessing device %03d on bus %03d ...\n", devnum, busnum); + devnum = libusb_get_device_address(dev); + busnum = libusb_get_bus_number(dev); + SHOW_PROGRESS(output,"Access device %03d on bus %03d\n", devnum, busnum); } - devh = usb_open(dev); + libusb_open(dev, &devh); if (devh == NULL) { - SHOW_PROGRESS(output,"Error opening the device. Aborting.\n\n"); + SHOW_PROGRESS(output,"Error opening the device. Abort\n\n"); exit(1); } } /* Get current configuration of default device - * A configuration value of -1 denotes a quirky device which has - * trouble determining the current configuration. Just use the first - * branch (which may be incorrect) + * A configuration value of -1 helps with quirky devices which have + * trouble determining the current configuration. We are just using the + * current config branch then. + * This affects only single-configuration devices so it's no problem. + * The dispatcher is using this always if no change of configuration + * is required for switching */ if (Configuration > -1) currentConfig = get_current_configuration(devh); else { - SHOW_PROGRESS(output,"Skipping the check for the current configuration\n"); + SHOW_PROGRESS(output,"Skip the check for the current configuration\n"); currentConfig = 0; } + libusb_get_device_descriptor(dev, &descriptor); + defaultClass = descriptor.bDeviceClass; + libusb_get_config_descriptor(dev, 0, &config); + if (Interface == -1) + Interface = config->interface[0].altsetting[0].bInterfaceNumber; + SHOW_PROGRESS(output,"Use interface number %d\n", Interface); + /* Get class of default device/interface */ - defaultClass = dev->descriptor.bDeviceClass; - interfaceClass = get_interface0_class(dev, currentConfig); + interfaceClass = get_interface_class(config, Interface); + libusb_free_config_descriptor(config); if (interfaceClass == -1) { - fprintf(stderr, "Error: getting the interface class failed. Aborting.\n\n"); + fprintf(stderr, "Error: Could not get class of interface %d. Does it exist? Abort\n\n",Interface); exit(1); } @@ -485,63 +574,70 @@ defaultClass = 8; } - if (Interface == -1) - Interface = dev->config[0].interface[0].altsetting[0].bInterfaceNumber; - SHOW_PROGRESS(output,"Using first interface: 0x%02x\n", Interface); + if (strlen(MessageContent) && strncmp("55534243",MessageContent,8) == 0) + if (defaultClass != 8) { + fprintf(stderr, "Error: can't use storage command in MessageContent with interface %d;\n" + " interface class is %d, expected 8. Abort\n\n", Interface, defaultClass); + exit(1); + } /* Check or get endpoints */ - if (strlen(MessageContent) || InquireDevice || CiscoMode) { + if (strlen(MessageContent) || StandardEject || InquireDevice || ModeMap & CISCO_MODE) { if (!MessageEndpoint) MessageEndpoint = find_first_bulk_output_endpoint(dev); if (!MessageEndpoint) { - fprintf(stderr,"Error: message endpoint not given or found. Aborting.\n\n"); + fprintf(stderr,"Error: message endpoint not given or found. Abort\n\n"); exit(1); } if (!ResponseEndpoint) ResponseEndpoint = find_first_bulk_input_endpoint(dev); if (!ResponseEndpoint) { - fprintf(stderr,"Error: response endpoint not given or found. Aborting.\n\n"); + fprintf(stderr,"Error: response endpoint not given or found. Abort\n\n"); exit(1); } - SHOW_PROGRESS(output,"Using endpoints 0x%02x (out) and 0x%02x (in)\n", MessageEndpoint, ResponseEndpoint); + SHOW_PROGRESS(output,"Use endpoints 0x%02x (out) and 0x%02x (in)\n", MessageEndpoint, ResponseEndpoint); } - if (!MessageEndpoint || !ResponseEndpoint) - if (InquireDevice && defaultClass == 0x08) { - SHOW_PROGRESS(output,"Endpoints not found, skipping SCSI inquiry\n"); - InquireDevice = 0; - } - if (InquireDevice && show_progress) { if (defaultClass == 0x08) { - SHOW_PROGRESS(output,"Inquiring device details; driver will be detached ...\n"); + SHOW_PROGRESS(output,"Inquire device details; driver will be detached ...\n"); detachDriver(); if (deviceInquire() >= 0) InquireDevice = 2; } else - SHOW_PROGRESS(output,"Not a storage device, skipping SCSI inquiry\n"); + SHOW_PROGRESS(output,"Not a storage device, skip SCSI inquiry\n"); } deviceDescription(); if (show_progress) { - printf("\nUSB description data (for identification)\n"); - printf("-------------------------\n"); - printf("Manufacturer: %s\n", imanufact); - printf(" Product: %s\n", iproduct); - printf(" Serial No.: %s\n", iserial); - printf("-------------------------\n"); + fprintf(output,"\nUSB description data (for identification)\n"); + fprintf(output,"-------------------------\n"); + fprintf(output,"Manufacturer: %s\n", imanufact); + fprintf(output," Product: %s\n", iproduct); + fprintf(output," Serial No.: %s\n", iserial); + fprintf(output,"-------------------------\n"); } - /* Some scenarios are exclusive, so check for unwanted combinations */ - specialMode = DetachStorageOnly + HuaweiMode + SierraMode + SonyMode + QisdaMode + KobilMode - + SequansMode + MobileActionMode + CiscoMode; - if ( specialMode > 1 ) { - SHOW_PROGRESS(output,"Invalid mode combination. Check your configuration. Aborting.\n\n"); + /* Special modes are exclusive, so check for illegal combinations. + * More than one bit set? + */ + if ( ModeMap & (ModeMap-1) ) { + fprintf(output,"Multiple special modes selected; check configuration. Abort\n\n"); exit(1); } - if ( !specialMode && !strlen(MessageContent) && AltSetting == -1 && Configuration == 0 ) - SHOW_PROGRESS(output,"Warning: no switching method given.\n"); + if ((strlen(MessageContent) || StandardEject) && ModeMap ) { + MessageContent[0] = '\0'; + StandardEject = 0; + fprintf(output,"Warning: MessageContent/StandardEject ignored; can't combine with special mode\n"); + } + + if (StandardEject && (strlen(MessageContent2) || strlen(MessageContent3))) { + fprintf(output,"Warning: MessageContent2/3 ignored; only one allowed with StandardEject\n"); + } + + if ( !ModeMap && !strlen(MessageContent) && AltSetting == -1 && !Configuration && !StandardEject ) + SHOW_PROGRESS(output,"Warning: no switching method given. See documentation\n"); /* * The switching actions @@ -549,13 +645,13 @@ if (sysmode) { openlog("usb_modeswitch", 0, LOG_SYSLOG); - syslog(LOG_NOTICE, "switching device %04x:%04x on %03d/%03d", DefaultVendor, DefaultProduct, busnum, devnum); + syslog(LOG_NOTICE, "switch device %04x:%04x on %03d/%03d", DefaultVendor, DefaultProduct, busnum, devnum); } - if (DetachStorageOnly) { - SHOW_PROGRESS(output,"Only detaching storage driver for switching ...\n"); + if (ModeMap & DETACHONLY_MODE) { + SHOW_PROGRESS(output,"Detach storage driver as switching method ...\n"); if (InquireDevice == 2) { - SHOW_PROGRESS(output," Any driver was already detached for inquiry\n"); + SHOW_PROGRESS(output," Any driver was already detached for inquiry. Do nothing\n"); } else { ret = detachDriver(); if (ret == 2) @@ -563,47 +659,67 @@ } } - if (HuaweiMode) { + if(ModeMap & HUAWEI_MODE) { switchHuaweiMode(); } - if (SierraMode) { + if(ModeMap & SIERRA_MODE) { switchSierraMode(); } - if (GCTMode) { + if(ModeMap & GCT_MODE) { detachDriver(); switchGCTMode(); } - if (QisdaMode) { + if(ModeMap & QISDA_MODE) { switchQisdaMode(); } - if(KobilMode) { + if(ModeMap & KOBIL_MODE) { detachDriver(); switchKobilMode(); } - if (SequansMode) { + if(ModeMap & QUANTA_MODE) { + switchQuantaMode(); + } + if(ModeMap & SEQUANS_MODE) { switchSequansMode(); } - if(MobileActionMode) { + if(ModeMap & MOBILEACTION_MODE) { switchActionMode(); } - if(CiscoMode) { + if(ModeMap & CISCO_MODE) { detachDriver(); switchCiscoMode(); } - if (SonyMode) { + if(ModeMap & BLACKBERRY_MODE) { + detachDriver(); + switchBlackberryMode(); + } + if(ModeMap & PANTECH_MODE) { + detachDriver(); + switchPantechMode(); + } + if(ModeMap & SONY_MODE) { if (CheckSuccess) - SHOW_PROGRESS(output,"Note: ignoring CheckSuccess. Separate checks for Sony mode\n"); + SHOW_PROGRESS(output,"Note: CheckSuccess ignored; Sony mode does separate checks\n"); CheckSuccess = 0; /* separate and implied success control */ sonySuccess = switchSonyMode(); } - if (strlen(MessageContent) && MessageEndpoint) { - if (specialMode == 0) { - if (InquireDevice != 2) - detachDriver(); - switchSendMessage(); - } else - SHOW_PROGRESS(output,"Warning: ignoring MessageContent. Can't combine with special mode\n"); + if (StandardEject) { + SHOW_PROGRESS(output,"Sending standard EJECT sequence\n"); + detachDriver(); + if (MessageContent[0] != '\0') + strcpy(MessageContent3, MessageContent); + else + MessageContent3[0] = '\0'; + + strcpy(MessageContent,"5553424312345678000000000000061e000000000000000000000000000000"); + strcpy(MessageContent2,"5553424312345679000000000000061b000000020000000000000000000000"); + NeedResponse = 1; + switchSendMessage(); + } else if (strlen(MessageContent)) { + if (InquireDevice != 2) + detachDriver(); + switchSendMessage(); } if (Configuration > 0) { @@ -617,7 +733,7 @@ } } } else { - SHOW_PROGRESS(output,"Target configuration %d already active. Doing nothing\n", currentConfig); + SHOW_PROGRESS(output,"Target configuration %d found. Do nothing\n", currentConfig); } } @@ -627,19 +743,18 @@ /* No "removal" check if these are set */ if ((Configuration > 0 || AltSetting > -1) && !ResetUSB) { - usb_close(devh); + libusb_close(devh); devh = 0; } if (ResetUSB) { resetUSB(); - usb_close(devh); devh = 0; } if (CheckSuccess) { if (searchMode == SEARCH_BUSDEV && sysmode) { - SHOW_PROGRESS(output,"Bus/dev search active, referring success check to wrapper. Bye.\n\n"); + SHOW_PROGRESS(output,"Bus/dev search active, refer success check to wrapper. Bye!\n\n"); printf("ok:busdev\n"); goto CLOSING; } @@ -657,26 +772,26 @@ if (sysmode) printf("fail:\n"); } else { - if (SonyMode) + if (ModeMap & SONY_MODE) if (sonySuccess) { if (sysmode) { syslog(LOG_NOTICE, "switched S.E. MD400 to modem mode"); printf("ok:\n"); /* ACM device, no driver action */ } - SHOW_PROGRESS(output,"-> device should be stable now. Bye.\n\n"); + SHOW_PROGRESS(output,"-> device should be stable now. Bye!\n\n"); } else { if (sysmode) printf("fail:\n"); - SHOW_PROGRESS(output,"-> switching was probably not completed. Bye.\n\n"); + SHOW_PROGRESS(output,"-> switching was probably not completed. Bye!\n\n"); } else - SHOW_PROGRESS(output,"-> Run lsusb to note any changes. Bye.\n\n"); + SHOW_PROGRESS(output,"-> Run lsusb to note any changes. Bye!\n\n"); } CLOSING: if (sysmode) closelog(); if (devh) - usb_close(devh); + libusb_close(devh); exit(0); } @@ -684,14 +799,20 @@ /* Get descriptor strings if available (identification details) */ void deviceDescription () { - int ret; char* c; memset (imanufact, ' ', DESCR_MAX); memset (iproduct, ' ', DESCR_MAX); memset (iserial, ' ', DESCR_MAX); - if (dev->descriptor.iManufacturer) { - ret = usb_get_string_simple(devh, dev->descriptor.iManufacturer, imanufact, DESCR_MAX); + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(dev, &descriptor); + + int iManufacturer = descriptor.iManufacturer; + int iProduct = descriptor.iProduct; + int iSerialNumber = descriptor.iSerialNumber; + + if (iManufacturer) { + ret = libusb_get_string_descriptor_ascii(devh, iManufacturer, (unsigned char *)imanufact, DESCR_MAX); if (ret < 0) fprintf(stderr, "Error: could not get description string \"manufacturer\"\n"); } else @@ -700,8 +821,8 @@ if (c) memset((void*)c, '\0', 1); - if (dev->descriptor.iProduct) { - ret = usb_get_string_simple(devh, dev->descriptor.iProduct, iproduct, DESCR_MAX); + if (iProduct) { + ret = libusb_get_string_descriptor_ascii(devh, iProduct, (unsigned char *)iproduct, DESCR_MAX); if (ret < 0) fprintf(stderr, "Error: could not get description string \"product\"\n"); } else @@ -710,8 +831,8 @@ if (c) memset((void*)c, '\0', 1); - if (dev->descriptor.iSerialNumber) { - ret = usb_get_string_simple(devh, dev->descriptor.iSerialNumber, iserial, DESCR_MAX); + if (iSerialNumber) { + ret = libusb_get_string_descriptor_ascii(devh, iSerialNumber, (unsigned char *)iserial, DESCR_MAX); if (ret < 0) fprintf(stderr, "Error: could not get description string \"serial number\"\n"); } else @@ -733,7 +854,7 @@ }; char *command; char data[36]; - int i, ret; + int i; command = malloc(31); if (command == NULL) { @@ -743,67 +864,121 @@ memcpy(command, inquire_msg, sizeof (inquire_msg)); - ret = usb_claim_interface(devh, Interface); + ret = libusb_claim_interface(devh, Interface); if (ret != 0) { - SHOW_PROGRESS(output," Could not claim interface (error %d). Skipping device inquiry\n", ret); + SHOW_PROGRESS(output," Could not claim interface (error %d). Skip device inquiry\n", ret); goto out; } - usb_clear_halt(devh, MessageEndpoint); + libusb_clear_halt(devh, MessageEndpoint); - ret = usb_bulk_write(devh, MessageEndpoint, (char *)command, 31, 0); + ret = usb_bulk_io(devh, MessageEndpoint, (char *)command, 31, 0); if (ret < 0) { - SHOW_PROGRESS(output," Could not send INQUIRY message (error %d)\n", ret); + SHOW_PROGRESS(output," INQUIRY message failed (error %d)\n", ret); goto out; } - ret = usb_bulk_read(devh, ResponseEndpoint, data, 36, 0); + ret = usb_bulk_io(devh, ResponseEndpoint, data, 36, 0); if (ret < 0) { - SHOW_PROGRESS(output," Could not get INQUIRY response (error %d)\n", ret); + SHOW_PROGRESS(output," INQUIRY response failed (error %d)\n", ret); goto out; } - i = usb_bulk_read(devh, ResponseEndpoint, command, 13, 0); + i = usb_bulk_io(devh, ResponseEndpoint, command, 13, 0); - printf("\nSCSI inquiry data (for identification)\n"); - printf("-------------------------\n"); + fprintf(output,"\nSCSI inquiry data (for identification)\n"); + fprintf(output,"-------------------------\n"); - printf(" Vendor String: "); + fprintf(output," Vendor String: "); for (i = 8; i < 16; i++) printf("%c",data[i]); - printf("\n"); + fprintf(output,"\n"); - printf(" Model String: "); + fprintf(output," Model String: "); for (i = 16; i < 32; i++) printf("%c",data[i]); - printf("\n"); + fprintf(output,"\n"); - printf("Revision String: "); + fprintf(output,"Revision String: "); for (i = 32; i < 36; i++) printf("%c",data[i]); - printf("\n-------------------------\n"); + fprintf(output,"\n-------------------------\n"); out: if (strlen(MessageContent) == 0) - usb_clear_halt(devh, MessageEndpoint); - usb_release_interface(devh, Interface); + libusb_clear_halt(devh, MessageEndpoint); + libusb_release_interface(devh, Interface); free(command); return ret; } +/* Auxiliary function used by the wrapper */ +int findMBIMConfig(int vendor, int product, int mode) +{ + struct libusb_device **devs; + int resultConfig=0; + int i=0, j; + + if (libusb_get_device_list(ctx, &devs) < 0) { + perror("Libusb could not access USB. Abort"); + return 0; + } + + SHOW_PROGRESS(output,"Search USB devices ...\n"); + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(dev, &descriptor); + + if (mode == SEARCH_BUSDEV) { + if ((libusb_get_bus_number(dev) != busnum) || + (libusb_get_device_address(dev) != devnum)) { + continue; + } else { + if (descriptor.idVendor != vendor) + continue; + if (product != descriptor.idProduct) + continue; + } + } + SHOW_PROGRESS(output,"Found device, search for MBIM configuration...\n"); + + // No check if there is only one configuration + if (descriptor.bNumConfigurations < 2) + return -1; + + // Checking all interfaces of all configurations + for (j=0; jbConfigurationValue; + for (i=0; ibNumInterfaces; i++) { + if ( config->interface[i].altsetting[0].bInterfaceClass == 2 ) + if ( config->interface[i].altsetting[0].bInterfaceSubClass == 0x0e ) { + // found MBIM interface in this configuration + libusb_free_config_descriptor(config); + return resultConfig; + } + } + libusb_free_config_descriptor(config); + } + return -1; + } + return 0; +} + void resetUSB () { int success; int bpoint = 0; if (show_progress) { - printf("Resetting usb device "); + fprintf(output,"Reset USB device "); fflush(stdout); } - sleep( 1 ); do { - success = usb_reset(devh); + success = libusb_reset_device(devh); if ( ((bpoint % 10) == 0) && show_progress ) { - printf("."); + fprintf(output,"."); fflush(stdout); } bpoint++; @@ -812,9 +987,9 @@ } while (success < 0); if ( success ) { - SHOW_PROGRESS(output,"\n Reset failed. Can be ignored if device switched OK.\n"); + SHOW_PROGRESS(output,"\n Device reset failed.\n"); } else - SHOW_PROGRESS(output,"\n OK, device was reset\n"); + SHOW_PROGRESS(output,"\n Device was reset\n"); } @@ -831,16 +1006,16 @@ // if (MessageContent2[0] != '\0' || MessageContent3[0] != '\0') // NeedResponse = 1; - SHOW_PROGRESS(output,"Setting up communication with interface %d\n", Interface); + SHOW_PROGRESS(output,"Set up interface %d\n", Interface); if (InquireDevice != 2) { - ret = usb_claim_interface(devh, Interface); + ret = libusb_claim_interface(devh, Interface); if (ret != 0) { - SHOW_PROGRESS(output," Could not claim interface (error %d). Skipping message sending\n", ret); + SHOW_PROGRESS(output," Could not claim interface (error %d). Skip message sending\n", ret); return 0; } } - usb_clear_halt(devh, MessageEndpoint); - SHOW_PROGRESS(output,"Using endpoint 0x%02x for message sending ...\n", MessageEndpoint); + libusb_clear_halt(devh, MessageEndpoint); + SHOW_PROGRESS(output,"Use endpoint 0x%02x for message sending ...\n", MessageEndpoint); if (show_progress) fflush(stdout); @@ -854,11 +1029,11 @@ if (NeedResponse) { if ( strstr(msg[i],cmdHead) != NULL ) { // UFI command - SHOW_PROGRESS(output,"Reading the response to message %d (CSW) ...\n", i+1); + SHOW_PROGRESS(output,"Read the response to message %d (CSW) ...\n", i+1); ret = read_bulk(ResponseEndpoint, ByteString, 13); } else { // Other bulk transfer - SHOW_PROGRESS(output,"Reading the response to message %d ...\n", i+1); + SHOW_PROGRESS(output,"Read the response to message %d ...\n", i+1); ret = read_bulk(ResponseEndpoint, ByteString, strlen(msg[i])/2 ); } if (ret < 0) @@ -866,207 +1041,176 @@ } } - SHOW_PROGRESS(output,"Resetting response endpoint 0x%02x\n", ResponseEndpoint); - ret = usb_clear_halt(devh, ResponseEndpoint); + SHOW_PROGRESS(output,"Reset response endpoint 0x%02x\n", ResponseEndpoint); + ret = libusb_clear_halt(devh, ResponseEndpoint); if (ret) SHOW_PROGRESS(output," Could not reset endpoint (probably harmless): %d\n", ret); - SHOW_PROGRESS(output,"Resetting message endpoint 0x%02x\n", MessageEndpoint); - ret = usb_clear_halt(devh, MessageEndpoint); + SHOW_PROGRESS(output,"Reset message endpoint 0x%02x\n", MessageEndpoint); + ret = libusb_clear_halt(devh, MessageEndpoint); if (ret) SHOW_PROGRESS(output," Could not reset endpoint (probably harmless): %d\n", ret); - usleep(200000); + usleep(50000); + if (ReleaseDelay) { - SHOW_PROGRESS(output,"Blocking the interface for %d ms before releasing ...\n", ReleaseDelay); + SHOW_PROGRESS(output,"Wait for %d ms before releasing interface ...\n", ReleaseDelay); usleep(ReleaseDelay*1000); } - ret = usb_release_interface(devh, Interface); + ret = libusb_release_interface(devh, Interface); if (ret) goto skip; return 1; skip: - SHOW_PROGRESS(output," Device is gone, skipping any further commands\n"); - usb_close(devh); + SHOW_PROGRESS(output," Device is gone, skip any further commands\n"); + libusb_close(devh); devh = 0; return 2; } + int switchConfiguration () { int count = SWITCH_CONFIG_MAXTRIES; - int ret; - SHOW_PROGRESS(output,"Changing configuration to %i ...\n", Configuration); - while (((ret = usb_set_configuration(devh, Configuration)) < 0) && --count) { - SHOW_PROGRESS(output," Device is busy, trying to detach kernel driver\n"); + SHOW_PROGRESS(output,"Change configuration to %i ...\n", Configuration); + while (((ret = libusb_set_configuration(devh, Configuration)) < 0) && --count) { + SHOW_PROGRESS(output," Device is busy, try to detach kernel driver\n"); detachDriver(); } - if (ret == 0 ) { + if (ret < 0 ) { + SHOW_PROGRESS(output," Changing the configuration failed (error %d). Try to continue\n", ret); + return 0; + } else { SHOW_PROGRESS(output," OK, configuration set\n"); return 1; } - SHOW_PROGRESS(output," Setting the configuration returned error %d. Trying to continue\n", ret); - return 0; } - int switchAltSetting () { - int ret; - SHOW_PROGRESS(output,"Changing to alt setting %i ...\n", AltSetting); - ret = usb_claim_interface(devh, Interface); - ret = usb_set_altinterface(devh, AltSetting); - usb_release_interface(devh, Interface); - if (ret != 0) { - SHOW_PROGRESS(output," Changing to alt setting returned error %d. Trying to continue\n", ret); + SHOW_PROGRESS(output,"Change to alt setting %i ...\n", AltSetting); + ret = libusb_claim_interface(devh, Interface); + ret = libusb_set_interface_alt_setting(devh, Interface, AltSetting); + libusb_release_interface(devh, Interface); + if (ret < 0) { + SHOW_PROGRESS(output," Change to alt setting returned error %d. Try to continue\n", ret); return 0; - } else { - SHOW_PROGRESS(output," OK, changed to alt setting\n"); + } else return 1; - } } void switchHuaweiMode () { - int ret; - SHOW_PROGRESS(output,"Sending Huawei control message ...\n"); - ret = usb_control_msg(devh, USB_TYPE_STANDARD + USB_RECIP_DEVICE, USB_REQ_SET_FEATURE, 00000001, 0, buffer, 0, 1000); + SHOW_PROGRESS(output,"Send old Huawei control message ...\n"); + ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE, \ + LIBUSB_REQUEST_SET_FEATURE, 00000001, 0, (unsigned char *)buffer, 0, 1000); if (ret != 0) { - fprintf(stderr, "Error: sending Huawei control message failed (error %d). Aborting.\n\n", ret); + fprintf(stderr, "Error: Huawei control message failed (error %d). Abort\n\n", ret); exit(1); - } else - SHOW_PROGRESS(output," OK, Huawei control message sent\n"); + } } void switchSierraMode () { - int ret; - - SHOW_PROGRESS(output,"Trying to send Sierra control message\n"); - ret = usb_control_msg(devh, 0x40, 0x0b, 00000001, 0, buffer, 0, 1000); - if (ret != 0) { - fprintf(stderr, "Error: sending Sierra control message failed (error %d). Aborting.\n\n", ret); + SHOW_PROGRESS(output,"Send Sierra control message\n"); + ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR, 0x0b, 00000001, 0, (unsigned char *)buffer, 0, 1000); + if (ret == LIBUSB_ERROR_PIPE) { + SHOW_PROGRESS(output," communication with device stopped. May have switched modes anyway\n"); + return; + } + if (ret < 0) { + fprintf(stderr, "Error: Sierra control message failed (error %d). Abort\n\n", ret); exit(1); - } else - SHOW_PROGRESS(output," OK, Sierra control message sent\n"); + } } void switchGCTMode () { - int ret; - - ret = usb_claim_interface(devh, Interface); + ret = libusb_claim_interface(devh, Interface); if (ret != 0) { - SHOW_PROGRESS(output," Could not claim interface (error %d). Skipping GCT sequence \n", ret); + SHOW_PROGRESS(output," Could not claim interface (error %d). Skip GCT sequence\n", ret); return; } - - SHOW_PROGRESS(output,"Sending GCT control message 1 ...\n"); - ret = usb_control_msg(devh, 0xa1, 0xa0, 0, Interface, buffer, 1, 1000); - SHOW_PROGRESS(output,"Sending GCT control message 2 ...\n"); - ret = usb_control_msg(devh, 0xa1, 0xfe, 0, Interface, buffer, 1, 1000); - SHOW_PROGRESS(output," OK, GCT control messages sent\n"); - usb_release_interface(devh, Interface); + SHOW_PROGRESS(output,"Send GCT control message 1 ...\n"); + ret = libusb_control_transfer(devh, 0xa1, 0xa0, 0, Interface, (unsigned char *)buffer, 1, 1000); + if (ret < 0) { + SHOW_PROGRESS(output," GCT control message 1 failed (error %d), continue anyway ...\n", ret); + } + SHOW_PROGRESS(output,"Send GCT control message 2 ...\n"); + ret = libusb_control_transfer(devh, 0xa1, 0xfe, 0, Interface, (unsigned char *)buffer, 1, 1000); + if (ret < 0) { + SHOW_PROGRESS(output," GCT control message 2 failed (error %d). Abort\n\n", ret); + } + libusb_release_interface(devh, Interface); + if (ret < 0) + exit(1); } -int switchKobilMode() { - int ret; - - SHOW_PROGRESS(output,"Sending Kobil control message ...\n"); - ret = usb_control_msg(devh, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, 0x88, 0, 0, buffer, 8, 1000); - if (ret != 0) { - fprintf(stderr, "Error: sending Kobil control message failed (error %d). Aborting.\n\n", ret); +void switchKobilMode() { + SHOW_PROGRESS(output,"Send Kobil control message ...\n"); + ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN, + 0x88, 0, 0, (unsigned char *)buffer, 8, 1000); + if (ret < 0) { + fprintf(stderr, "Error: Kobil control message failed (error %d). Abort\n\n", ret); exit(1); - } else - SHOW_PROGRESS(output," OK, Kobil control message sent\n"); - return 1; + } } -int switchQisdaMode () { - int ret; - +void switchQisdaMode () { SHOW_PROGRESS(output,"Sending Qisda control message ...\n"); memcpy(buffer, "\x05\x8c\x04\x08\xa0\xee\x20\x00\x5c\x01\x04\x08\x98\xcd\xea\xbf", 16); - ret = usb_control_msg(devh, 0x40, 0x04, 00000000, 0, buffer, 16, 1000); - if (ret != 0) { - fprintf(stderr, "Error: sending Qisda control message failed (error %d). Aborting.\n\n", ret); + ret = libusb_control_transfer(devh, 0x40, 0x04, 0, 0, (unsigned char *)buffer, 16, 1000); + if (ret < 0) { + fprintf(stderr, "Error: Qisda control message failed (error %d). Abort\n\n", ret); exit(1); - } else - SHOW_PROGRESS(output," OK, Qisda control message sent\n"); - return 1; + } } -int switchSonyMode () -{ - int i, found, ret; - detachDriver(); - - if (CheckSuccess) { - printf("Note: CheckSuccess pointless with Sony mode, disabling\n"); - CheckSuccess = 0; - } - - SHOW_PROGRESS(output,"Trying to send Sony control message\n"); - ret = usb_control_msg(devh, 0xc0, 0x11, 2, 0, buffer, 3, 100); +void switchQuantaMode() { + SHOW_PROGRESS(output,"Send Quanta control message ...\n"); + ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN, + 0xff, 0, 0, (unsigned char *)buffer, 0, 1000); if (ret < 0) { - fprintf(stderr, "Error: sending Sony control message failed (error %d). Aborting.\n\n", ret); + SHOW_PROGRESS(output,"Error: Quanta control message failed (error %d). Abort\n\n", ret); exit(1); - } else - SHOW_PROGRESS(output," OK, control message sent, waiting for device to return ...\n"); + } +} - usb_close(devh); - devh = 0; - /* Now waiting for the device to reappear */ - devnum=-1; - busnum=-1; - i=0; - dev = 0; - while ( dev == 0 && i < 30 ) { - if ( i > 5 ) { - usb_find_busses(); - usb_find_devices(); - dev = search_devices(&found, DefaultVendor, DefaultProduct, "\0", TargetClass, 0, SEARCH_TARGET); - } - if ( dev != 0 ) - break; - sleep(1); - if (show_progress) { - printf("#"); - fflush(stdout); - } - i++; - } - SHOW_PROGRESS(output,"\n After %d seconds:",i); - if ( dev ) { - SHOW_PROGRESS(output," device came back, proceeding\n"); - devh = usb_open( dev ); - if (devh == 0) { - fprintf(stderr, "Error: could not get handle on device\n"); - return 0; - } - } else { - SHOW_PROGRESS(output," device still gone, cancelling\n"); - return 0; +void switchBlackberryMode () +{ + SHOW_PROGRESS(output,"Send Blackberry control message 1 ...\n"); + ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN, + 0xb1, 0x0000, 0, (unsigned char *)buffer, 8, 1000); + if (ret != 8) { + fprintf(stderr, "Error: Blackberry control message 1 failed (result %d)\n", ret); + } + SHOW_PROGRESS(output,"Send Blackberry control message 2 ...\n"); + ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_IN, + 0xa9, 0x000e, 0, (unsigned char *)buffer, 2, 1000); + if (ret != 2) { + fprintf(stderr, "Error: Blackberry control message 2 failed (result %d). Abort\n\n", ret); + exit(1); } - sleep(1); +} + - SHOW_PROGRESS(output,"Sending Sony control message again ...\n"); - ret = usb_control_msg(devh, 0xc0, 0x11, 2, 0, buffer, 3, 100); +void switchPantechMode() +{ + SHOW_PROGRESS(output,"Send Pantech control message ...\n"); + ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, 0x70, 2, 0, (unsigned char *)buffer, 0, 1000); if (ret < 0) { - fprintf(stderr, "Error: sending Sony control message (2) failed (error %d)\n", ret); - return 0; + SHOW_PROGRESS(output," Error: Pantech control message failed (error %d). Abort\n\n", ret); + exit(1); } - SHOW_PROGRESS(output," OK, control message sent\n"); - return 1; } @@ -1077,50 +1221,53 @@ #define MOBILE_ACTION_READLOOP1 63 #define MOBILE_ACTION_READLOOP2 73 -int switchActionMode () +/* If anyone can test the MobileAction cable - I bet this + * function (which is confirmed working) can be greatly + * simplified ... + */ + +void switchActionMode () { int i; - SHOW_PROGRESS(output,"Sending MobileAction control sequence ...\n"); + SHOW_PROGRESS(output,"Send MobileAction control sequence ...\n"); memcpy(buffer, "\xb0\x04\x00\x00\x02\x90\x26\x86", SIZE); - usb_control_msg(devh, USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x0300, 0, buffer, SIZE, 1000); + libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_CLASS + LIBUSB_RECIPIENT_INTERFACE, 0x09, 0x0300, 0, (unsigned char *)buffer, SIZE, 1000); memcpy(buffer, "\xb0\x04\x00\x00\x02\x90\x26\x86", SIZE); - usb_control_msg(devh, USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x0300, 0, buffer, SIZE, 1000); - usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); - usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); + libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_CLASS + LIBUSB_RECIPIENT_INTERFACE, 0x09, 0x0300, 0, (unsigned char *)buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000); memcpy(buffer, "\x37\x01\xfe\xdb\xc1\x33\x1f\x83", SIZE); - usb_interrupt_write(devh, EP_OUT, buffer, SIZE, 1000); - usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000); memcpy(buffer, "\x37\x0e\xb5\x9d\x3b\x8a\x91\x51", SIZE); - usb_interrupt_write(devh, EP_OUT, buffer, SIZE, 1000); - usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000); memcpy(buffer, "\x34\x87\xba\x0d\xfc\x8a\x91\x51", SIZE); - usb_interrupt_write(devh, EP_OUT, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000); for (i=0; i < MOBILE_ACTION_READLOOP1; i++) { - usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000); } memcpy(buffer, "\x37\x01\xfe\xdb\xc1\x33\x1f\x83", SIZE); - usb_interrupt_write(devh, EP_OUT, buffer, SIZE, 1000); - usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000); memcpy(buffer, "\x37\x0e\xb5\x9d\x3b\x8a\x91\x51", SIZE); - usb_interrupt_write(devh, EP_OUT, buffer, SIZE, 1000); - usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000); memcpy(buffer, "\x34\x87\xba\x0d\xfc\x8a\x91\x51", SIZE); - usb_interrupt_write(devh, EP_OUT, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000); for (i=0; i < MOBILE_ACTION_READLOOP2; i++) { - usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000); } memcpy(buffer, "\x33\x04\xfe\x00\xf4\x6c\x1f\xf0", SIZE); - usb_interrupt_write(devh, EP_OUT, buffer, SIZE, 1000); - usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000); memcpy(buffer, "\x32\x07\xfe\xf0\x29\xb9\x3a\xf0", SIZE); - ret = usb_interrupt_write(devh, EP_OUT, buffer, SIZE, 1000); - usb_interrupt_read(devh, EP_IN, buffer, SIZE, 1000); + ret = usb_interrupt_io(devh, EP_OUT, buffer, SIZE, 1000); + usb_interrupt_io(devh, EP_IN, buffer, SIZE, 1000); if (ret < 0) { SHOW_PROGRESS(output," MobileAction control sequence did not complete\n Last error was %d\n",ret); - return 1; } else { SHOW_PROGRESS(output," MobileAction control sequence complete\n"); - return 0; } } @@ -1132,26 +1279,20 @@ #define SQN_MASS_STORAGE_MODE 0x01 #define SQN_CUSTOM_DEVICE_MODE 0x02 -int switchSequansMode() { - int ret; +void switchSequansMode() { - SHOW_PROGRESS(output,"Sending Sequans vendor request\n"); - ret = usb_control_msg(devh, USB_TYPE_VENDOR | USB_RECIP_DEVICE, SQN_SET_DEVICE_MODE_REQUEST, SQN_CUSTOM_DEVICE_MODE, 0, buffer, 0, 1000); - if (ret != 0) { - fprintf(stderr, "Error: sending Sequans request failed (error %d). Aborting.\n\n", ret); + SHOW_PROGRESS(output,"Send Sequans control message\n"); + ret = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SQN_SET_DEVICE_MODE_REQUEST, SQN_CUSTOM_DEVICE_MODE, 0, (unsigned char *)buffer, 0, 1000); + if (ret < 0) { + fprintf(stderr, "Error: Sequans request failed (error %d). Abort\n\n", ret); exit(1); - } else - SHOW_PROGRESS(output," OK, Sequans request was sent\n"); - - return 1; + } } -int switchCiscoMode() { - int ret, i; +void switchCiscoMode() { + int i; char* msg[11]; - SHOW_PROGRESS(output,"Preparing for sending Cisco message sequence\n"); - msg[0] = "55534243f83bcd810002000080000afd000000030000000100000000000000"; msg[1] = "55534243984300820002000080000afd000000070000000100000000000000"; msg[2] = "55534243984300820000000000000afd000100071000000000000000000000"; @@ -1164,13 +1305,13 @@ msg[9] = "55534243d84c04820000000000000afd000300241300000000000000000000"; msg[10] = "55534243d84c04820000000000000afd000110732400000000000000000000"; - SHOW_PROGRESS(output,"Setting up communication with interface %d\n", Interface); - ret = usb_claim_interface(devh, Interface); - if (ret != 0) { - SHOW_PROGRESS(output," Could not claim interface (error %d). Skipping message sending\n", ret); - return 0; + SHOW_PROGRESS(output,"Set up Cisco interface %d\n", Interface); + ret = libusb_claim_interface(devh, Interface); + if (ret < 0) { + SHOW_PROGRESS(output," Could not claim interface (error %d). Abort\n", ret); + exit(1); } -// usb_clear_halt(devh, MessageEndpoint); +// libusb_clear_halt(devh, MessageEndpoint); if (show_progress) fflush(output); @@ -1178,70 +1319,130 @@ if ( sendMessage(msg[i], i+1) ) goto skip; - SHOW_PROGRESS(output,"Reading the response (CSW) to bulk message %d ...\n",i+1); + SHOW_PROGRESS(output," Read the response (CSW) to bulk message %d ...\n",i+1); ret = read_bulk(ResponseEndpoint, ByteString, 13); if (ret < 0) goto skip; } if (ReleaseDelay) { - SHOW_PROGRESS(output,"Blocking the interface for %d ms before releasing ...\n", ReleaseDelay); + SHOW_PROGRESS(output,"Wait for %d ms before releasing interface ...\n", ReleaseDelay); usleep(ReleaseDelay*1000); } - ret = usb_release_interface(devh, Interface); - if (ret) + ret = libusb_release_interface(devh, Interface); + if (ret < 0) goto skip; - return 1; + return; skip: - SHOW_PROGRESS(output,"Device returned error, skipping any further commands\n"); - usb_close(devh); + SHOW_PROGRESS(output,"Device returned error %d, skip further commands\n", ret); + libusb_close(devh); devh = 0; - return 2; } -/* Detach driver - */ -int detachDriver() + +int switchSonyMode () { - int ret; + int i, found; + detachDriver(); -#ifndef LIBUSB_HAS_GET_DRIVER_NP - printf(" Cant't do driver detection and detaching on this platform.\n"); - return 2; -#endif + if (CheckSuccess) { + CheckSuccess = 0; + } - SHOW_PROGRESS(output,"Looking for active driver ...\n"); - ret = usb_get_driver_np(devh, Interface, buffer, BUF_SIZE); - if (ret != 0) { - SHOW_PROGRESS(output," No driver found. Either detached before or never attached\n"); - return 1; + SHOW_PROGRESS(output,"Send Sony control message\n"); + ret = libusb_control_transfer(devh, 0xc0, 0x11, 2, 0, (unsigned char *)buffer, 3, 100); + if (ret < 0) { + fprintf(stderr, "Error: Sony control message failed (error %d). Abort\n\n", ret); + exit(1); + } else + SHOW_PROGRESS(output," OK, control message sent, wait for device to return ...\n"); + + libusb_close(devh); + devh = 0; + + /* Now waiting for the device to reappear */ + devnum=-1; + busnum=-1; + i=0; + dev = 0; + while ( dev == 0 && i < 30 ) { + if ( i > 5 ) { + dev = search_devices(&found, DefaultVendor, DefaultProductList, TargetClass, 0, SEARCH_TARGET); + } + if ( dev != 0 ) + break; + sleep(1); + if (show_progress) { + fprintf(output,"#"); + fflush(stdout); + } + i++; } - if (strncmp("dummy",buffer,5) == 0) { - SHOW_PROGRESS(output," OK, driver found; name unknown, limitation of libusb1\n"); - strcpy(buffer,"unkown"); + SHOW_PROGRESS(output,"\n After %d seconds:",i); + if ( dev ) { + SHOW_PROGRESS(output," device came back, proceed\n"); + libusb_open(dev, &devh); + if (devh == 0) { + fprintf(stderr, "Error: could not get handle on device\n"); + return 0; + } } else { - SHOW_PROGRESS(output," OK, driver found (\"%s\")\n", buffer); + SHOW_PROGRESS(output," device still gone, abort\n"); + return 0; } + sleep(1); -#ifndef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP - SHOW_PROGRESS(output," Can't do driver detaching on this platform\n"); - return 2; -#endif + SHOW_PROGRESS(output,"Send Sony control message again ...\n"); + ret = libusb_control_transfer(devh, 0xc0, 0x11, 2, 0, (unsigned char *)buffer, 3, 100); + if (ret < 0) { + fprintf(stderr, "Error: Sony control message (2) failed (error %d)\n", ret); + return 0; + } + SHOW_PROGRESS(output," OK, control message sent\n"); + return 1; +} + + +/* Detach driver + */ +int detachDriver() +{ + // Driver already detached during SCSI inquiry ? + if (InquireDevice == 2) + return 1; + SHOW_PROGRESS(output,"Looking for active driver ...\n"); + ret = libusb_kernel_driver_active(devh, 0); + if (ret == LIBUSB_ERROR_NOT_SUPPORTED) { + fprintf(output," Can't do driver detection on this platform.\n"); + return 2; + } + if (ret < 0) { + fprintf(output," Driver check failed with error %d. Try to continue\n", ret); + return 2; + } + if (ret == 0) { + SHOW_PROGRESS(output," No active driver found. Detached before or never attached\n"); + return 1; + } - ret = usb_detach_kernel_driver_np(devh, Interface); + ret = libusb_detach_kernel_driver(devh, Interface); + if (ret == LIBUSB_ERROR_NOT_SUPPORTED) { + fprintf(output," Can't do driver detaching on this platform.\n"); + return 2; + } if (ret == 0) { - SHOW_PROGRESS(output," OK, driver \"%s\" detached\n", buffer); + SHOW_PROGRESS(output," OK, driver detached\n"); } else - SHOW_PROGRESS(output," Driver \"%s\" detach failed with error %d. Trying to continue\n", buffer, ret); + SHOW_PROGRESS(output," Driver detach failed (error %d). Try to continue\n", ret); return 1; } int sendMessage(char* message, int count) { - int message_length, ret; + int message_length; if (strlen(message) % 2 != 0) { fprintf(stderr, "Error: MessageContent %d hex string has uneven length. Skipping ...\n", count); @@ -1255,7 +1456,7 @@ SHOW_PROGRESS(output,"Trying to send message %d to endpoint 0x%02x ...\n", count, MessageEndpoint); fflush(output); ret = write_bulk(MessageEndpoint, ByteString, message_length); - if (ret == -19) + if (ret == LIBUSB_ERROR_NO_DEVICE) return 1; return 0; @@ -1264,10 +1465,10 @@ int checkSuccess() { - int i=0, ret; + int i=0; int newTargetCount, success=0; - SHOW_PROGRESS(output,"\nChecking for mode switch (max. %d times, once per second) ...\n", CheckSuccess); + SHOW_PROGRESS(output,"\nCheck for mode switch (max. %d times, once per second) ...\n", CheckSuccess); sleep(1); /* If target parameters are given, don't check for vanished device @@ -1275,7 +1476,7 @@ * storage device stays active */ if ((TargetVendor || TargetClass) && devh) { - usb_close(devh); + libusb_close(devh); devh = 0; } @@ -1295,18 +1496,18 @@ /* Test if default device still can be accessed; positive result does * not necessarily mean failure */ - SHOW_PROGRESS(output," Waiting for original device to vanish ...\n"); + SHOW_PROGRESS(output," Wait for original device to vanish ...\n"); - ret = usb_claim_interface(devh, Interface); - usb_release_interface(devh, Interface); + ret = libusb_claim_interface(devh, Interface); + libusb_release_interface(devh, Interface); if (ret < 0) { SHOW_PROGRESS(output," Original device can't be accessed anymore. Good.\n"); - usb_close(devh); + libusb_close(devh); devh = 0; break; } if (i == CheckSuccess-1) { - SHOW_PROGRESS(output," Original device still present after the timeout\n\nMode switch most likely failed. Bye.\n\n"); + SHOW_PROGRESS(output," Original device still present after the timeout\n\nMode switch most likely failed. Bye!\n\n"); } else sleep(1); } @@ -1318,38 +1519,30 @@ * description is read for syslog message */ for (i=i; i < CheckSuccess; i++) { - SHOW_PROGRESS(output," Searching for target devices ...\n"); - ret = usb_find_busses(); - if (ret >= 0) - ret = usb_find_devices(); - if (ret < 0) { - SHOW_PROGRESS(output,"Error: libusb1 bug, no more searching, try to work around\n"); - success = 3; - break; - } - dev = search_devices(&newTargetCount, TargetVendor, TargetProduct, TargetProductList, TargetClass, 0, SEARCH_TARGET); + SHOW_PROGRESS(output," Search for target devices ...\n"); + dev = search_devices(&newTargetCount, TargetVendor, TargetProductList, TargetClass, 0, SEARCH_TARGET); if (dev && (newTargetCount > targetDeviceCount)) { - printf("\nFound target device, now opening\n"); - devh = usb_open(dev); + fprintf(output,"\nFound target device, open it\n"); + libusb_open(dev, &devh); deviceDescription(); - usb_close(devh); + libusb_close(devh); devh = 0; if (verbose) { - printf("\nFound target device %03d on bus %03d\n", \ - dev->devnum, (int)strtol(dev->bus->dirname,NULL,10)); - printf("\nTarget device description data\n"); - printf("-------------------------\n"); - printf("Manufacturer: %s\n", imanufact); - printf(" Product: %s\n", iproduct); - printf(" Serial No.: %s\n", iserial); - printf("-------------------------\n"); + fprintf(output,"\nFound target device %03d on bus %03d\n", \ + libusb_get_device_address(dev), libusb_get_bus_number(dev)); + fprintf(output,"\nTarget device description data\n"); + fprintf(output,"-------------------------\n"); + fprintf(output,"Manufacturer: %s\n", imanufact); + fprintf(output," Product: %s\n", iproduct); + fprintf(output," Serial No.: %s\n", iserial); + fprintf(output,"-------------------------\n"); } - SHOW_PROGRESS(output," Found correct target device\n\nMode switch succeeded. Bye.\n\n"); + SHOW_PROGRESS(output," Found correct target device\n\nMode switch succeeded. Bye!\n\n"); success = 2; break; } if (i == CheckSuccess-1) { - SHOW_PROGRESS(output," No new devices in target mode or class found\n\nMode switch has failed. Bye.\n\n"); + SHOW_PROGRESS(output," No new devices in target mode or class found\n\nMode switch has failed. Bye!\n\n"); } else sleep(1); } @@ -1357,7 +1550,7 @@ /* No target data given, rely on the vanished device */ if (!devh) { SHOW_PROGRESS(output," (For a better success check provide target IDs or class)\n"); - SHOW_PROGRESS(output," Original device vanished after switching\n\nMode switch most likely succeeded. Bye.\n\n"); + SHOW_PROGRESS(output," Original device vanished after switching\n\nMode switch most likely succeeded. Bye!\n\n"); success = 1; } @@ -1389,40 +1582,37 @@ int write_bulk(int endpoint, char *message, int length) { - int ret; - ret = usb_bulk_write(devh, endpoint, message, length, 3000); + ret = usb_bulk_io(devh, endpoint, message, length, 3000); if (ret >= 0 ) { SHOW_PROGRESS(output," OK, message successfully sent\n"); } else - if (ret == -19) { + if (ret == LIBUSB_ERROR_NO_DEVICE) { SHOW_PROGRESS(output," Device seems to have vanished right after sending. Good.\n"); } else - SHOW_PROGRESS(output," Sending the message returned error %d. Trying to continue\n", ret); + SHOW_PROGRESS(output," Sending the message returned error %d. Try to continue\n", ret); return ret; } int read_bulk(int endpoint, char *buffer, int length) { - int ret; - ret = usb_bulk_read(devh, endpoint, buffer, length, 3000); - usb_bulk_read(devh, endpoint, buffer, 13, 100); + ret = usb_bulk_io(devh, endpoint, buffer, length, 3000); if (ret >= 0 ) { - SHOW_PROGRESS(output," OK, response successfully read (%d bytes).\n", ret); + SHOW_PROGRESS(output," Response successfully read (%d bytes).\n", ret); } else - if (ret == -19) { + if (ret == LIBUSB_ERROR_NO_DEVICE) { SHOW_PROGRESS(output," Device seems to have vanished after reading. Good.\n"); } else - SHOW_PROGRESS(output," Response reading got error %d\n", ret); + SHOW_PROGRESS(output," Response reading failed (error %d)\n", ret); return ret; } -void release_usb_device(int dummy) { - SHOW_PROGRESS(output,"Program cancelled by system. Bye.\n\n"); +void release_usb_device(int __attribute__((unused)) dummy) { + SHOW_PROGRESS(output,"Program cancelled by system. Bye!\n\n"); if (devh) { - usb_release_interface(devh, Interface); - usb_close(devh); + libusb_release_interface(devh, Interface); + libusb_close(devh); } if (sysmode) closelog(); @@ -1434,134 +1624,132 @@ /* Iterates over busses and devices, counts the ones which match the given * parameters and returns the last one of them */ -struct usb_device* search_devices( int *numFound, int vendor, int product, char* productList, int targetClass, int configuration, int mode) +struct libusb_device* search_devices( int *numFound, int vendor, char* productList, int targetClass, int configuration, int mode) { - struct usb_bus *bus; char *listcopy=NULL, *token, buffer[2]; - int devClass; - struct usb_device* right_dev = NULL; - struct usb_dev_handle *testdevh; + int devClass, product; + struct libusb_device* right_dev = NULL; + struct libusb_device_handle *testdevh; + struct libusb_device **devs; + int i=0; /* only target class given, target vendor and product assumed unchanged */ - if ( targetClass && !(vendor || product) ) { + if ( targetClass && !(vendor || strlen(productList)) ) { vendor = DefaultVendor; - product = DefaultProduct; + productList = DefaultProductList; } *numFound = 0; /* Sanity check */ - if (!vendor || (!product && productList == '\0') ) + if (!vendor || productList == '\0') return NULL; - if (productList != '\0') - listcopy = malloc(strlen(productList)+1); + listcopy = malloc(strlen(productList)+1); - for (bus = usb_get_busses(); bus; bus = bus->next) { - if (mode == SEARCH_BUSDEV) - if (busnum != (int)strtol(bus->dirname,NULL,10)) + if (libusb_get_device_list(ctx, &devs) < 0) { + perror("Libusb failed to get USB access!"); + return 0; + } + + while ((dev = devs[i++]) != NULL) { + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(dev, &descriptor); + + if (mode == SEARCH_BUSDEV) { + if ((libusb_get_bus_number(dev) != busnum) || + (libusb_get_device_address(dev) != devnum)) continue; - struct usb_device *dev; - for (dev = bus->devices; dev; dev = dev->next) { - if (mode == SEARCH_BUSDEV) { - if (dev->devnum != devnum) - continue; - else - SHOW_PROGRESS(output," bus/device number matched\n"); + else + SHOW_PROGRESS(output," bus/device number matched\n"); + } + + if (verbose) + fprintf (output," found USB ID %04x:%04x\n", + descriptor.idVendor, descriptor.idProduct); + if (descriptor.idVendor != vendor) + continue; + if (verbose) + fprintf (output," vendor ID matched\n"); + + strcpy(listcopy, productList); + token = strtok(listcopy, ","); + while (token != NULL) { + if (strlen(token) != 4) { + SHOW_PROGRESS(output,"Error: entry in product ID list has wrong length: %s. Ignored\n", token); + goto NextToken; } - if (verbose) - fprintf (output," searching devices, found USB ID %04x:%04x\n", dev->descriptor.idVendor, dev->descriptor.idProduct); - if (dev->descriptor.idVendor != vendor) - continue; - if (verbose) - fprintf (output," found matching vendor ID\n"); - // product list given - if ( strlen(productList) ) { - strcpy(listcopy, productList); - token = strtok(listcopy, ","); - while (token != NULL) { - if (strlen(token) != 4) { - SHOW_PROGRESS(output,"Error: entry in product ID list has wrong length: %s. Ignoring\n", token); - goto NextToken; - } - if ( hexstr2bin(token, buffer, strlen(token)/2) == -1) { - SHOW_PROGRESS(output,"Error: entry in product ID list is not a hex string: %s. Ignoring\n", token); - goto NextToken; - } - product = 0; - product += (unsigned char)buffer[0]; - product <<= 8; - product += (unsigned char)buffer[1]; - if (product == dev->descriptor.idProduct) { + if ( hexstr2bin(token, buffer, strlen(token)/2) == -1) { + SHOW_PROGRESS(output,"Error: entry in product ID list is not a hex string: %s. Ignored\n", token); + goto NextToken; + } + product = 0; + product += (unsigned char)buffer[0]; + product <<= 8; + product += (unsigned char)buffer[1]; + if (product == descriptor.idProduct) { + SHOW_PROGRESS(output," product ID matched\n"); + + if (targetClass != 0) { + // TargetClass is set, check class of first interface + struct libusb_device_descriptor descriptor; + libusb_get_device_descriptor(dev, &descriptor); + devClass = descriptor.bDeviceClass; + struct libusb_config_descriptor *config; + libusb_get_config_descriptor(dev, 0, &config); + int ifaceClass = config->interface[0].altsetting[0].bInterfaceClass; + libusb_free_config_descriptor(config); + if (devClass == 0) + devClass = ifaceClass; + else + /* Check for some quirky devices */ + if (devClass != ifaceClass) + devClass = ifaceClass; + if (devClass == targetClass) { if (verbose) - fprintf (output," found matching product ID from list\n"); - (*numFound)++; - if (busnum == -1) + fprintf (output," target class %02x matches\n", targetClass); + if (mode == SEARCH_TARGET) { + (*numFound)++; right_dev = dev; - else - if (dev->devnum >= devnum && (int)strtol(dev->bus->dirname,NULL,10) == busnum) { - right_dev = dev; - TargetProduct = dev->descriptor.idProduct; - break; - } - } - NextToken: - token = strtok(NULL, ","); - } - /* Product ID is given */ - } else - if (product == dev->descriptor.idProduct) { - SHOW_PROGRESS(output," found matching product ID\n"); - if (targetClass == 0 && configuration < 1) { - (*numFound)++; - SHOW_PROGRESS(output," adding device\n"); - right_dev = dev; + if (verbose) + fprintf (output," count device\n"); + } else + if (verbose) + fprintf (output," device not counted, target class reached\n"); } else { - if (targetClass != 0) { - devClass = dev->descriptor.bDeviceClass; - if (devClass == 0) - devClass = dev->config[0].interface[0].altsetting[0].bInterfaceClass; - else - /* Check for some quirky devices */ - if (devClass != dev->config[0].interface[0].altsetting[0].bInterfaceClass) - devClass = dev->config[0].interface[0].altsetting[0].bInterfaceClass; - if (devClass == targetClass) { - if (verbose) - fprintf (output," target class %02x matching\n", targetClass); - if (mode == SEARCH_TARGET) { - (*numFound)++; - right_dev = dev; - if (verbose) - fprintf (output," adding device\n"); - } else - if (verbose) - fprintf (output," not adding device\n"); - } else { - if (verbose) - fprintf (output," target class %02x not matching\n", targetClass); - if (mode == SEARCH_DEFAULT || mode == SEARCH_BUSDEV) { - (*numFound)++; - right_dev = dev; - if (verbose) - fprintf (output," adding device\n"); - } - } - } else { - // check configuration (only if no target class given) - testdevh = usb_open(dev); - int testconfig = get_current_configuration(testdevh); - if (testconfig != configuration) { - if (verbose) - fprintf (output," device configuration %d not matching parameter\n", testconfig); - (*numFound)++; - right_dev = dev; - if (verbose) - fprintf (output," adding device\n"); - } else - if (verbose) - fprintf (output," not adding device, target configuration already set\n"); + if (verbose) + fprintf (output," device class %02x not matching target\n", devClass); + if (mode == SEARCH_DEFAULT || mode == SEARCH_BUSDEV) { + (*numFound)++; + right_dev = dev; + if (verbose) + fprintf (output," count device\n"); } } + } else if (configuration > 0) { + // Configuration is set, check device configuration + libusb_open(dev, &testdevh); + int testconfig = get_current_configuration(testdevh); + if (testconfig != configuration) { + if (verbose) + fprintf (output," device configuration %d not matching target\n", testconfig); + (*numFound)++; + right_dev = dev; + if (verbose) + fprintf (output," count device\n"); + } else + if (verbose) + fprintf (output," device not counted, target configuration reached\n"); + } else { + // Neither TargetClass nor Configuration are set + (*numFound)++; + right_dev = dev; + if (mode == SEARCH_BUSDEV) + break; } + } + + NextToken: + token = strtok(NULL, ","); } } if (listcopy != NULL) @@ -1575,50 +1763,51 @@ /* Autodetect bulk endpoints (ab) */ -int find_first_bulk_output_endpoint(struct usb_device *dev) +int find_first_bulk_output_endpoint(struct libusb_device *dev) { int i; - struct usb_interface_descriptor *alt = &(dev->config[0].interface[0].altsetting[0]); - struct usb_endpoint_descriptor *ep; + struct libusb_config_descriptor *config; + libusb_get_config_descriptor(dev, 0, &config); + const struct libusb_interface_descriptor *alt = &(config[0].interface[0].altsetting[0]); + const struct libusb_endpoint_descriptor *ep; for(i=0;i < alt->bNumEndpoints;i++) { ep=&(alt->endpoint[i]); - if( ( (ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) && - ( (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT ) ) { + if( ( (ep->bmAttributes & LIBUSB_ENDPOINT_ADDRESS_MASK) == LIBUSB_TRANSFER_TYPE_BULK) && + ( (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == USB_DIR_OUT ) ) { return ep->bEndpointAddress; } } - + libusb_free_config_descriptor(config); return 0; } -int find_first_bulk_input_endpoint(struct usb_device *dev) +int find_first_bulk_input_endpoint(struct libusb_device *dev) { int i; - struct usb_interface_descriptor *alt = &(dev->config[0].interface[0].altsetting[0]); - struct usb_endpoint_descriptor *ep; - + struct libusb_config_descriptor *config; + libusb_get_config_descriptor(dev, 0, &config); + const struct libusb_interface_descriptor *alt = &(config[0].interface[0].altsetting[0]); + const struct libusb_endpoint_descriptor *ep; for(i=0;i < alt->bNumEndpoints;i++) { ep=&(alt->endpoint[i]); - if( ( (ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) && - ( (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN ) ) { + if( ( (ep->bmAttributes & LIBUSB_ENDPOINT_ADDRESS_MASK) == LIBUSB_TRANSFER_TYPE_BULK) && + ( (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == USB_DIR_IN ) ) { return ep->bEndpointAddress; } } - + libusb_free_config_descriptor(config); return 0; } -int get_current_configuration(struct usb_dev_handle* devh) +int get_current_configuration(struct libusb_device_handle* devh) { - int ret; - - SHOW_PROGRESS(output,"Getting the current device configuration ...\n"); - ret = usb_control_msg(devh, USB_DIR_IN + USB_TYPE_STANDARD + USB_RECIP_DEVICE, USB_REQ_GET_CONFIGURATION, 0, 0, buffer, 1, 1000); + SHOW_PROGRESS(output,"Get the current device configuration ...\n"); + ret = libusb_control_transfer(devh, USB_DIR_IN + LIBUSB_REQUEST_TYPE_STANDARD + LIBUSB_RECIPIENT_DEVICE, LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, (unsigned char *)buffer, 1, 1000); if (ret < 0) { // There are quirky devices which fail to respond properly to this command - fprintf(stderr, "Error getting the current configuration (error %d). Assuming configuration 1.\n", ret); + fprintf(stderr, "Error getting the current configuration (error %d). Assume configuration 1\n", ret); if (Configuration) { fprintf(stderr, " No configuration setting possible for this device.\n"); Configuration = 0; @@ -1630,52 +1819,48 @@ } } - -int get_interface0_class(struct usb_device *dev, int devconfig) +int get_interface_class(struct libusb_config_descriptor *cfg, int ifcNumber) { - /* Hack for quirky devices */ - if (devconfig == 0) - return dev->config[0].interface[0].altsetting[0].bInterfaceClass; - int i; - for (i=0; idescriptor.bNumConfigurations; i++) - if (dev->config[i].bConfigurationValue == devconfig) - return dev->config[i].interface[0].altsetting[0].bInterfaceClass; + for (i=0; ibNumInterfaces; i++) { +// SHOW_PROGRESS(output,"Test: looking at ifc %d, class is %d\n",i,cfg->interface[i].altsetting[0].bInterfaceClass); + if (cfg->interface[i].altsetting[0].bInterfaceNumber == ifcNumber) + return cfg->interface[i].altsetting[0].bInterfaceClass; + } return -1; } - /* Parameter parsing */ char* ReadParseParam(const char* FileName, char *VariableName) { static int numLines = 0; static char* ConfigBuffer[MAXLINES]; - char *VarName, *Comment=NULL, *Equal=NULL; char *FirstQuote, *LastQuote, *P1, *P2; - int Line=0, Len=0, Pos=0; + int Line=0; + unsigned Len=0, Pos=0; char Str[LINE_DIM], *token, *configPos; FILE *file = NULL; // Reading and storing input during the first call if (numLines==0) { if (strncmp(FileName,"##",2) == 0) { - if (verbose) fprintf(output,"\nReading long config from command line\n"); + if (verbose) fprintf(output,"\nRead long config from command line\n"); // "Embedded" configuration data configPos = (char*)FileName; token = strtok(configPos, "\n"); strncpy(Str,token,LINE_DIM-1); } else { if (strcmp(FileName, "stdin")==0) { - if (verbose) fprintf(output,"\nReading long config from stdin\n"); + if (verbose) fprintf(output,"\nRead long config from stdin\n"); file = stdin; } else { - if (verbose) fprintf(output,"\nReading config file: %s\n", FileName); + if (verbose) fprintf(output,"\nRead config file: %s\n", FileName); file=fopen(FileName, "r"); } if (file==NULL) { - fprintf(stderr, "Error: Could not find file %s\n\n", FileName); + fprintf(stderr, "Error: Could not find file %s. Abort\n\n", FileName); exit(1); } else { token = fgets(Str, LINE_DIM-1, file); @@ -1732,7 +1917,7 @@ Pos=strspn (Str, " \t"); if (Pos==strlen(Str)) { fprintf(stderr, "Error reading parameters from file %s - Missing variable name:\n%s\n", FileName, Str); - goto Next; // No function name + goto Next; } while ((P1=strrchr(Str, ' '))!=NULL or (P2=strrchr(Str, '\t'))!=NULL) if (P1!=NULL) *P1='\0'; @@ -1742,7 +1927,7 @@ Pos=strspn (Equal, " \t"); if (Pos==strlen(Equal)) { fprintf(stderr, "Error reading parameter from file %s - Missing value:\n%s\n", FileName, Str); - goto Next; // No function name + goto Next; } Equal+=Pos; @@ -1801,29 +1986,31 @@ void printVersion() { char* version = VERSION; - printf("\n * usb_modeswitch: handle USB devices with multiple modes\n" - " * Version %s (C) Josua Dietze 2012\n" - " * Based on libusb0 (0.1.12 and above)\n\n" + fprintf(output,"\n * usb_modeswitch: handle USB devices with multiple modes\n" + " * Version %s (C) Josua Dietze 2013\n" + " * Based on libusb1/libusbx\n\n" " ! PLEASE REPORT NEW CONFIGURATIONS !\n\n", version); } void printHelp() { - fprintf (output,"\nUsage: usb_modeswitch [] [-c filename]\n\n" + fprintf(output,"\nUsage: usb_modeswitch [] [-c filename]\n\n" " -h, --help this help\n" " -e, --version print version information and exit\n" + " -j, --find-mbim return config no. with MBIM interface, exit\n\n" " -v, --default-vendor NUM vendor ID of original mode (mandatory)\n" " -p, --default-product NUM product ID of original mode (mandatory)\n" " -V, --target-vendor NUM target mode vendor ID (optional)\n" " -P, --target-product NUM target mode product ID (optional)\n" " -C, --target-class NUM target mode device class (optional)\n" - " -b, --busnum NUM system bus number of device (for hard ID)\n" - " -g, --devnum NUM system device number (for hard ID)\n" + " -b, --bus-num NUM system bus number of device (for hard ID)\n" + " -g, --device-num NUM system device number (for hard ID)\n" " -m, --message-endpoint NUM direct the message transfer there (optional)\n" " -M, --message-content message to send (hex number as string)\n" " -2 , -3 additional messages to send (-n recommended)\n" " -n, --need-response read response to the message transfer (CSW)\n" " -r, --response-endpoint NUM read response from there (optional)\n" + " -K, --std-eject send standard EJECT sequence\n" " -d, --detach-only detach the active driver, no further action\n" " -H, --huawei-mode apply a special procedure\n" " -S, --sierra-mode apply a special procedure\n" @@ -1834,12 +2021,13 @@ " -T, --kobil-mode apply a special procedure\n" " -L, --cisco-mode apply a special procedure\n" " -B, --qisda-mode apply a special procedure\n" + " -E, --quanta-mode apply a special procedure\n" " -R, --reset-usb reset the device after all other actions\n" " -Q, --quiet don't show progress or error messages\n" " -W, --verbose print all settings and debug output\n" " -D, --sysmode specific result and syslog message\n" " -s, --success switching result check with timeout\n" - " -I, --no-inquire do not get SCSI attributes (default on)\n\n" + " -I, --inquire retrieve SCSI attributes initially\n\n" " -c, --config-file load long configuration from file\n\n" " -t, --stdinput read long configuration from stdin\n\n" " -f, --long-config get long configuration from string\n\n" diff -Nru usb-modeswitch-1.2.3+repack0/usb_modeswitch.conf usb-modeswitch-2.1.0+repack0/usb_modeswitch.conf --- usb-modeswitch-1.2.3+repack0/usb_modeswitch.conf 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/usb_modeswitch.conf 2014-02-08 22:14:11.000000000 +0000 @@ -16,4 +16,12 @@ # Enable logging (results in a extensive report file in /var/log, named # "usb_modeswitch_" and probably others -EnableLogging=1 +EnableLogging=0 + + +# Optional increase of "delay_use" for the usb-storage driver; there are hints +# that a recent kernel default change to 1 sec. may lead to problems, particu- +# larly with USB 3.0 ports. Set this to at least 3 (seconds) in that case. +# Does nothing if the current system value is same or higher + +#SetStorageDelay=3 diff -Nru usb-modeswitch-1.2.3+repack0/usb_modeswitch.h usb-modeswitch-2.1.0+repack0/usb_modeswitch.h --- usb-modeswitch-1.2.3+repack0/usb_modeswitch.h 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/usb_modeswitch.h 2014-02-08 22:14:11.000000000 +0000 @@ -1,9 +1,9 @@ /* This file is part of usb_modeswitch, a mode switching tool for controlling - flip flop (multiple device) USB gear + flip flop (multiple mode) USB devices - Version 1.2.3, 2012/01/28 - Copyright (C) 2007 - 2012 Josua Dietze + Version 2.1.0, 2014/01/28 + Copyright (C) 2007 - 2014 Josua Dietze Config file parsing stuff borrowed from Guillaume Dargaud (http://www.gdargaud.net/Hack/SourceCode.html) @@ -22,9 +22,8 @@ */ - #include -#include +#include void readConfigFile(const char *configFilename); void printConfig(); @@ -32,26 +31,30 @@ int switchConfiguration(); int switchAltSetting(); void switchHuaweiMode(); + void switchSierraMode(); void switchGCTMode(); -int switchKobilMode(); -int switchQisdaMode(); -int switchSequansMode(); -int switchActionMode(); +void switchKobilMode(); +void switchQisdaMode(); +void switchQuantaMode(); +void switchSequansMode(); +void switchActionMode(); +void switchBlackberryMode(); +void switchPantechMode(); +void switchCiscoMode(); int switchSonyMode(); -int switchCiscoMode(); int detachDriver(); int checkSuccess(); int sendMessage(char* message, int count); int write_bulk(int endpoint, char *message, int length); int read_bulk(int endpoint, char *buffer, int length); void release_usb_device(int dummy); -struct usb_device* search_devices( int *numFound, int vendor, int product, char* productList, - int targetClass, int configuration, int mode); -int find_first_bulk_output_endpoint(struct usb_device *dev); -int find_first_bulk_input_endpoint(struct usb_device *dev); -int get_current_configuration(struct usb_dev_handle* devh); -int get_interface0_class(struct usb_device *dev, int devconfig); +struct libusb_device* search_devices( int *numFound, int vendor, char* productList, + int targetClass, int configuration, int mode); +int find_first_bulk_output_endpoint(struct libusb_device *dev); +int find_first_bulk_input_endpoint(struct libusb_device *dev); +int get_current_configuration(struct libusb_device_handle* devh); +int get_interface_class(struct libusb_config_descriptor *cfg, int ifcNumber); char* ReadParseParam(const char* FileName, char *VariableName); int hex2num(char c); int hex2byte(const char *hex); @@ -63,6 +66,8 @@ int deviceInquire(); void resetUSB(); void release_usb_device(int dummy); +int findMBIMConfig(int vendor, int product, int mode); + // Boolean #define and && @@ -105,3 +110,7 @@ if ((TempPP=ReadParseParam((ParamFileName), #B))!=NULL) \ B=(toupper(TempPP[0])=='Y' || toupper(TempPP[0])=='T'|| TempPP[0]=='1'); else B=0 +#define ParseParamBoolMap(ParamFileName, B, M, Const) \ + if ((TempPP=ReadParseParam((ParamFileName), #B))!=NULL) \ + if (toupper(TempPP[0])=='Y' || toupper(TempPP[0])=='T'|| TempPP[0]=='1') \ + M=M+Const diff -Nru usb-modeswitch-1.2.3+repack0/usb_modeswitch.sh usb-modeswitch-2.1.0+repack0/usb_modeswitch.sh --- usb-modeswitch-1.2.3+repack0/usb_modeswitch.sh 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/usb_modeswitch.sh 2014-02-08 22:14:11.000000000 +0000 @@ -1,5 +1,5 @@ #!/bin/sh -# part of usb_modeswitch 1.2.3 +# part of usb_modeswitch 2.1.0 device_in() { if [ ! -e /var/lib/usb_modeswitch/$1 ]; then @@ -56,8 +56,8 @@ if [ -e "$id_attr" ]; then echo "$v_id $p_id ff" > $id_attr else - modprobe -r usbserial - modprobe usbserial "vendor=0x$v_id" "product=0x$p_id" + modprobe -r usbserial 2>/dev/null + modprobe usbserial "vendor=0x$v_id" "product=0x$p_id" 2>/dev/null fi fi ) & @@ -75,13 +75,19 @@ esac exec 1<&- 2<&- 5<&- 7<&- ( -count=120 +count=20 while [ $count != 0 ]; do if [ ! -e "/usr/sbin/usb_modeswitch_dispatcher" ]; then sleep 1 count=$(($count - 1)) else - exec usb_modeswitch_dispatcher --switch-mode $1 $0 & + 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 + exec /usr/bin/systemctl --no-block start usb_modeswitch@$1.service + else + exec /usr/sbin/usb_modeswitch_dispatcher --switch-mode $1 & + fi exit 0 fi done diff -Nru usb-modeswitch-1.2.3+repack0/usb_modeswitch.tcl usb-modeswitch-2.1.0+repack0/usb_modeswitch.tcl --- usb-modeswitch-1.2.3+repack0/usb_modeswitch.tcl 2012-01-29 16:52:26.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/usb_modeswitch.tcl 2014-02-08 22:14:11.000000000 +0000 @@ -9,8 +9,8 @@ # the mode switching program with the matching parameter # file from /usr/share/usb_modeswitch # -# Part of usb-modeswitch-1.2.3 package -# (C) Josua Dietze 2009-2012 +# Part of usb-modeswitch-2.1.0 package +# (C) Josua Dietze 2009-2014 set arg0 [lindex $argv 0] if [regexp {\.tcl$} $arg0] { @@ -26,16 +26,16 @@ set flags(logging) 0 set flags(noswitching) 0 - -#set env(PATH) "/bin:/sbin:/usr/bin:/usr/sbin" +set flags(stordelay) 0 # Execution starts at file bottom proc {Main} {argv argc} { -global scsi usb config match device flags settings +global scsi usb config match device flags setup devdir loginit -set loginit [ParseGlobalConfig] +set flags(logwrite) 0 +Log "[ParseGlobalConfig]" # The facility to add a symbolic link pointing to the # ttyUSB port which provides interrupt transfer, i.e. @@ -52,37 +52,44 @@ SafeExit } -set argList [split [lindex $argv 1] /] - -if [string length [lindex $argList 1]] { - set device [lindex $argList 1] +if {[lindex $argv 0] == "--switch-systemd"} { + set device [string trim [lindex $argv 1] "/-"] + set device [regsub {/} $device "-"] + set argList [list "" $device] + Log "\nStarted via systemd" } else { - set device "noname" + if {[lindex $argv 0] == "--switch-upstart"} { + Log "\nStarted via upstart" + } + set argList [split [lindex $argv 1] /] + if [string length [lindex $argList 1]] { + set device [lindex $argList 1] + } else { + set device "noname" + } +} +if {$flags(stordelay) > 0} { + SetStorageDelay $flags(stordelay) } -Log "Raw args from udev: [lindex $argv 1]\n\n$loginit" +Log "Raw args from udev: [lindex $argv 1]\n" if {$device == "noname"} { - Log "No data from udev. Exiting" - SafeExit -} - -if {[lindex $argv 0] != "--switch-mode"} { - Log "No command given. Exiting" + Log "\nNo data from udev. Exit" SafeExit } -if {![regexp /lib/udev/usb_modeswitch [lindex $argv 2]]} { - Log "Dispatcher was not run from call script. Exiting" +if {![regexp -- {--switch-} [lindex $argv 0]]} { + Log "\nNo command given. Exit" SafeExit } -set settings(dbdir) /usr/share/usb_modeswitch -set settings(dbdir_etc) /etc/usb_modeswitch.d +set setup(dbdir) /usr/share/usb_modeswitch +set setup(dbdir_etc) /etc/usb_modeswitch.d -if {![file exists $settings(dbdir)] && ![file exists $settings(dbdir_etc)]} { - Log "Error: no config database found in /usr/share or /etc. Exiting" +if {![file exists $setup(dbdir)] && ![file exists $setup(dbdir_etc)]} { + Log "\nError: no config database found in /usr/share or /etc. Exit" SafeExit } set bindir /usr/sbin @@ -91,23 +98,25 @@ set devList2 {} -# arg 0: the bus id for the device (udev: %b) +# arg 0: the bus id for the device (udev: %b), often ommitted # arg 1: the "kernel name" for the device (udev: %k) # -# Both together give the top directory where the path -# to the SCSI attributes can be determined (further down) -# Addendum: older kernel/udev version seem to differ in -# providing these attributes - or not. So more probing -# is needed +# Used to determine the top directory for the device in sysfs +set ifChk 0 if {[string length [lindex $argList 0]] == 0} { if {[string length [lindex $argList 1]] == 0} { - Log "No device number values given from udev! Exiting" + Log "No device number values given from udev! Exit" SafeExit } else { if {![regexp {(.*?):} [lindex $argList 1] d dev_top]} { - Log "Could not determine top device dir from udev values! Exiting" - SafeExit + if [regexp {([0-9]+-[0-9]+\.?[0-9]*.*)} [lindex $argList 1] d dev_top] { + # new udev rules file, got to check class of first interface + set ifChk 1 + } else { + Log "Could not determine device dir from udev values! Exit" + SafeExit + } } } } else { @@ -117,11 +126,31 @@ set devdir /sys/bus/usb/devices/$dev_top if {![file isdirectory $devdir]} { - Log "Top device directory not found ($devdir)! Exiting" + Log "Top device directory not found ($devdir)! Exit" SafeExit } -Log "Using top device dir $devdir" -set ifdir "[file tail $devdir]:1.0" +Log "Use top device dir $devdir" + +set iface 0 +if $ifChk { + Log "Check class of first interface ..." + set iclass [IfClass 0] + if {$iface < 0} { + Log " No access to interface 0. Exit" + SafeExit + } + if {$iclass == 8} { + Log " Device is in install mode." + } else { + # No install mode, so no logging at all + exit + } +} +set ifdir [file tail [IfDir $iface]] +regexp {:([0-9]+\.[0-9]+)$} $ifdir d iface + +set flags(logwrite) 1 +Log "Use interface $iface" # Mapping of the short string identifiers (in the config @@ -139,12 +168,12 @@ # Now reading the USB attributes if {![ReadUSBAttrs $devdir]} { - Log "USB attributes not found in sysfs tree. Exiting" + Log "USB attributes not found in sysfs tree. Exit" SafeExit } if $flags(logging) { - Log "----------------\nUSB values from sysfs:" + Log "\n----------------\nUSB values from sysfs:" foreach attr {manufacturer product serial} { Log " $attr\t$usb($attr)" } @@ -152,8 +181,8 @@ } if $flags(noswitching) { - Log "\nSwitching globally disabled. Exiting\n" SysLog "usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)" + Log "\nSwitching globally disabled. Exit" SafeExit } @@ -171,7 +200,7 @@ set configList [ConfigGet conflist $usb(idVendor):$usb(idProduct)] if {[llength $configList] == 0} { - Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exiting" + Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exit" SafeExit } @@ -182,7 +211,7 @@ } } if $scsiNeeded { - if [ReadSCSIAttrs $devdir:1.0] { + if [ReadSCSIAttrs $devdir:$iface] { Log "----------------\nSCSI values from sysfs:" foreach attr {vendor model rev} { Log " $attr\t$scsi($attr)" @@ -192,7 +221,7 @@ Log "Could not get SCSI attributes, exclude devices with SCSI match" } } else { - Log "SCSI attributes not needed, moving on" + Log "SCSI attributes not needed, move on" } # General wait - this is important @@ -207,9 +236,9 @@ # skipping installer leftovers if [regexp {\.(dpkg|rpm)} $configuration] {continue} - Log "checking config: $configuration" + Log "Check config: $configuration" if [MatchDevice $configuration] { - Log "! matched. Reading config data" + Log "! matched. Read config data" if [string length $usb(busnum)] { set busParam "-b [string trimleft $usb(busnum) 0]" set devParam "-g [string trimleft $usb(devnum) 0]" @@ -221,16 +250,37 @@ ParseDeviceConfig $configBuffer if {$config(waitBefore) == ""} { } else { - Log " waiting time set to $config(waitBefore) seconds" + Log "Delay time of $config(waitBefore) seconds" append config(waitBefore) "000" after $config(waitBefore) - Log " waiting is over, switching starts now" + Log " wait is over, start mode switch" + } + if {$config(noMBIMCheck)==0 && $usb(bNumConfigurations) > 1} { + Log "Device may have an MBIM configuration, check driver ..." + if [CheckMBIM] { + Log " driver for MBIM devices is available" + Log "Find MBIM configuration number ..." + if [catch {set cfgno [exec /usr/sbin/usb_modeswitch -j -Q $busParam $devParam -v $usb(idVendor) -p $usb(idProduct)]} err] { + Log "Error when trying to find MBIM configuration, switch to legacy modem mode" + } else { + set cfgno [string trim $cfgno] + if {$cfgno > 0} { + set config(Configuration) $cfgno + set config(driverModule) "" + set configBuffer "Configuration=$cfgno" + } else { + Log " No MBIM configuration found, switch to legacy modem mode" + } + } + } else { + Log " no MBIM driver found, switch to legacy modem mode" + } } # Now we are actually switching if $flags(logging) { - Log "Command to be run:\nusb_modeswitch -I -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f \$configBuffer" - set report [exec /usr/sbin/usb_modeswitch -I -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@ stdout] + Log "Command to be run:\nusb_modeswitch -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f \$configBuffer" + set report [exec /usr/sbin/usb_modeswitch -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@1] Log "\nVerbose debug output of usb_modeswitch and libusb follows" Log "(Note that some USB errors are to be expected in the process)" Log "--------------------------------" @@ -238,7 +288,7 @@ Log "--------------------------------" Log "(end of usb_modeswitch output)\n" } else { - set report [exec /usr/sbin/usb_modeswitch -I -Q -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@ stdout] + set report [exec /usr/sbin/usb_modeswitch -Q -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@1] } break } else { @@ -260,16 +310,16 @@ foreach attr [lsort [array names usb]] { Log " [format %-26s $attr:] $usb($attr)" } - Log "\nMode switching may have failed. Exiting\n" + Log "\nMode switching may have failed. Exit" SafeExit } } else { if {![file isdirectory $devdir]} { - Log "Device directory in sysfs is gone! Something went wrong, aborting" + Log "Device directory in sysfs is gone! Something went wrong, abort" SafeExit } if {![regexp {ok:} $report]} { - Log "\nCore program reported switching failure. Exiting\n" + Log "\nCore program reported switching failure. Exit" SafeExit } # Give the device another second if it's not fully back yet @@ -281,10 +331,10 @@ # Now checking for bound drivers (only for class 0xff) -if {$usb($ifdir/bInterfaceClass) != "" && [regexp {ok:} $report]} { +if {$config(driverModule) != "" && $usb($ifdir/bInterfaceClass) != "" && [regexp {ok:} $report]} { if {$usb($ifdir/bInterfaceClass) != "ff"} { set config(driverModule) "" - Log " No vendor-specific class found, skip driver checking" + Log " No vendor-specific class found, skip driver check" } } @@ -294,13 +344,13 @@ if {$config(driverModule) != ""} { if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} { if {![regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct)]} { - Log "No target vendor/product ID found or given, can't continue. Aborting" + Log "No target vendor/product ID found or given, can't continue. Abort" SafeExit } } # wait for any drivers to bind automatically after 1000 - Log "Now checking for bound driver ..." + Log "Now check for bound driver ..." if {![file exists $devdir/$ifdir/driver]} { Log " no driver has bound to interface 0 yet" AddToList link_list $usb(idVendor):$usb(idProduct) @@ -339,14 +389,14 @@ if [regexp {ok:$} $report] { # "NoDriverLoading" was set - Log "Doing no driver checking or binding for this device" + Log "No driver check or bind for this device" } # In newer kernels there is a switch to avoid the use of a device # reset (e.g. from usb-storage) which would possibly switch back # a mode-switching device to initial mode if [regexp {ok:} $report] { - Log "Checking for AVOID_RESET_QUIRK kernel attribute" + Log "Check for AVOID_RESET_QUIRK kernel attribute" if [file exists $devdir/avoid_reset_quirk] { if [catch {exec echo "1" >$devdir/avoid_reset_quirk 2>/dev/null} err] { Log " Error setting the attribute: $err" @@ -358,7 +408,7 @@ } } -Log "\nAll done, exiting\n" +Log "\nAll done, exit\n" SafeExit } @@ -370,7 +420,7 @@ global scsi set counter 0 set sysdir $topdir -Log "Checking storage tree in sysfs ..." +Log "Check storage tree in sysfs ..." while {$counter < 20} { Log " loop $counter/20" if {![file isdirectory $sysdir]} { @@ -402,7 +452,7 @@ return 0 } -Log "Reading SCSI values ..." +Log "Read SCSI values ..." foreach attr {vendor model rev} { if [file exists $sysdir/$attr] { set rc [open $sysdir/$attr r] @@ -466,9 +516,9 @@ set matchstring [lindex $tokenList 1] set blankstring "" regsub -all {_} $matchstring { } blankstring - Log "matching $match($id)" - Log " match string1 (exact): $matchstring" - Log " match string2 (blanks): $blankstring" + Log "match $match($id)" + Log " string1 (exact): $matchstring" + Log " string2 (blanks): $blankstring" Log " device string: [set $match($id)]" if {!([string match *$matchstring* [set $match($id)]] || [string match *$blankstring* [set $match($id)]])} { return 0 @@ -496,6 +546,7 @@ set rc [open $configFile r] while {![eof $rc]} { gets $rc line + if [regexp {^#} [string trim $line]] {continue} if [regexp {DisableSwitching\s*=\s*([^\s]+)} $line d val] { if [regexp -nocase {1|yes|true} $val] { set flags(noswitching) 1 @@ -506,9 +557,14 @@ set flags(logging) 1 } } + if [regexp {SetStorageDelay\s*=\s*([^\s]+)} $line d val] { + if [regexp {\d+} $val] { + set flags(stordelay) $val + } + } } -return "Using global config file: $configFile" +return "Use global config file: $configFile" } # end of proc {ParseGlobalConfig} @@ -524,6 +580,7 @@ set config(targetProduct) "" set config(targetClass) "" set config(Configuration) "" +set config(noMBIMCheck) 0 set config(checkSuccess) 20 set loadDriver 1 @@ -554,6 +611,9 @@ if [regexp -line {^[^#]*?WaitBefore.*?=.*?([0-9]+).*?$} $configContent d config(waitBefore)] { Log "config: WaitBefore set to $config(waitBefore)" } +if [regexp -line {^[^#]*?NoMBIMCheck.*?=.*?([0-9]+).*?$} $configContent d config(noMBIMCheck)] { + Log "config: noMBIMCheck set to $config(noMBIMCheck)" +} if [regexp -line {^[^#]*?NoDriverLoading.*?=.*?(1|yes|true).*?$} $configContent] { set loadDriver 0 Log "config: NoDriverLoading is set to active" @@ -582,17 +642,17 @@ proc ConfigGet {command config} { -global settings +global setup switch $command { conflist { # Unpackaged configs first; sorting is essential for priority - set configList [lsort -decreasing [glob -nocomplain $settings(dbdir_etc)/$config*]] - set configList [concat $configList [lsort -decreasing [glob -nocomplain $settings(dbdir)/$config*]]] - if [file exists $settings(dbdir)/configPack.tar.gz] { - Log "Found packed config collection $settings(dbdir)/configPack.tar.gz" - if [catch {set packedList [exec tar -tzf $settings(dbdir)/configPack.tar.gz 2>/dev/null]} err] { + set configList [lsort -decreasing [glob -nocomplain $setup(dbdir_etc)/$config*]] + set configList [concat $configList [lsort -decreasing [glob -nocomplain $setup(dbdir)/$config*]]] + if [file exists $setup(dbdir)/configPack.tar.gz] { + Log "Found packed config collection $setup(dbdir)/configPack.tar.gz" + if [catch {set packedList [exec tar -tzf $setup(dbdir)/configPack.tar.gz 2>/dev/null]} err] { Log "Error: problem opening config package; tar returned\n $err" return {} } @@ -609,12 +669,12 @@ conffile { if [regexp {^pack/} $config] { set config [regsub {pack/} $config {}] - Log "Extracting config $config from collection $settings(dbdir)/configPack.tar.gz" - set configContent [exec tar -xzOf $settings(dbdir)/configPack.tar.gz $config 2>/dev/null] + Log "Extract config $config from collection $setup(dbdir)/configPack.tar.gz" + set configContent [exec tar -xzOf $setup(dbdir)/configPack.tar.gz $config 2>/dev/null] } else { - if [regexp [list $settings(dbdir_etc)] $config] { - Log "Using config file from override folder $settings(dbdir_etc)" - SysLog "usb_modeswitch: using overriding config file $config; make sure this is intended" + if [regexp [list $setup(dbdir_etc)] $config] { + Log "Use config file from override folder $setup(dbdir_etc)" + SysLog "usb_modeswitch: use overriding config file $config; make sure this is intended" SysLog "usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files" } set rc [open $config r] @@ -630,34 +690,34 @@ proc {Log} {msg} { -global flags device +global flags device loginit + if {$flags(logging) == 0} {return} +#puts $msg +#return -if {![info exists flags(wc)]} { - if [catch {set flags(wc) [open /var/log/usb_modeswitch_$device w]} err] { - if [catch {set flags(wc) [open /dev/console w]} err] { - set flags(wc) "error" - return - } else { - puts $flags(wc) "Error opening log file ($err), redirect to console" - } +if $flags(logwrite) { + if [string length $loginit] { + exec echo "\nUSB_ModeSwitch log from [clock format [clock seconds]]" >/var/log/usb_modeswitch_$device + exec echo "$loginit" >>/var/log/usb_modeswitch_$device +#" + set loginit "" } - puts $flags(wc) "\n\nUSB_ModeSwitch log from [clock format [clock seconds]]\n" + exec echo $msg >>/var/log/usb_modeswitch_$device +} else { + append loginit "\n$msg" } -if {$flags(wc) == "error"} {return} -puts $flags(wc) $msg } # end of proc {Log} -# Closing the log file if open and exit +# Writing the log file and exit proc {SafeExit} {} { global flags -if [info exists flags(wc)] { - catch {close $flags(wc)} -} +set $flags(logwrite) 1 +Log "" exit } @@ -669,12 +729,12 @@ proc {hasInterrupt} {ifDir} { if {[llength [glob -nocomplain $ifDir/ttyUSB*]] == 0} { - Log " no ttyUSB interface - skip checking endpoints" + Log " no ttyUSB interface - skip endpoint check" return 0 } foreach epDir [glob -nocomplain $ifDir/ep_*] { set e [file tail $epDir] - Log " checking $e ..." + Log " check $e ..." if [file exists $epDir/type] { set rc [open $epDir/type r] set type [read $rc] @@ -698,7 +758,7 @@ set rawpath [file readlink $linkpath] set trimpath [regsub -all {\.\./} $rawpath {}] if [file isdirectory /sys/$trimpath] { - append loginit "\n Using path $path\n" + append loginit "\n Use path $path\n" set path /$trimpath } } @@ -707,7 +767,7 @@ if {![regexp {ttyUSB[0-9]+} $path myPort]} { if $flags(logging) { set device [clock clicks] - Log "$loginit\nThis is not a ttyUSB port. Aborting" + Log "$loginit\nThis is not a ttyUSB port. Abort" } return "" } @@ -716,13 +776,13 @@ Log "$loginit\nMy name is $myPort\n" if {![regexp {(.*?[0-9]+)\.([0-9]+)/ttyUSB} /sys$path d ifRoot ifNum]} { - Log "Could not find interface in path\n $path. Aborting" + Log "Could not find interface in path\n $path. Abort" return "" } set ifDir $ifRoot.$ifNum -Log "Checking my endpoints ...\n in $ifDir" +Log "Check my endpoints ...\n in $ifDir" if [hasInterrupt $ifDir] { Log "\n--> I am an interrupt port" set rightPort 1 @@ -736,7 +796,7 @@ # possible lower interfaces if { $rightPort && ($ifNum > 0) } { - Log "\nLooking for lower ports with interrupt endpoints" + Log "\nLook for lower ports with interrupt endpoints" for {set i 0} {$i < $ifNum} {incr i} { set ifDir $ifRoot.$i Log " in ifDir $ifDir ..." @@ -793,7 +853,7 @@ Log "Can't do anymore without module loader; get \"modtools\"!" return } - Log "\nTrying to load module \"$config(driverModule)\"" + Log "\nTry to load module \"$config(driverModule)\"" if [catch {set result [exec $loader -v $config(driverModule)]} err] { Log " Running \"$loader $config(driverModule)\" gave an error:\n $err" } else { @@ -811,8 +871,8 @@ incr i } if {$i < 50} { - Log "Trying to add ID to driver \"$config(driverModule)\"" - SysLog "usb_modeswitch: adding device ID $vid:$pid to driver \"$config(driverModule)\"" + Log "Try to add ID to driver \"$config(driverModule)\"" + SysLog "usb_modeswitch: add device ID $vid:$pid to driver \"$config(driverModule)\"" SysLog "usb_modeswitch: please report the device ID to the Linux USB developers!" if [catch {exec echo "$vid $pid ff" >$idfile} err] { Log " Error adding ID to driver:\n $err" @@ -821,16 +881,16 @@ } } else { Log " \"$idfile\" not found, check if kernel version is at least 2.6.27" - Log "Falling back to \"usbserial\"" + Log "Fall back to \"usbserial\"" set config(driverModule) usbserial - Log "\nTrying to unload driver \"usbserial\"" + Log "\nTry to unload driver \"usbserial\"" if [catch {exec $loader -r usbserial} err] { Log " Running \"$loader -r usbserial\" gave an error:\n $err" Log "No more fallbacks" return } after 50 - Log "\nTrying to load driver \"usbserial\" with device IDs" + Log "\nTry to load driver \"usbserial\" with device IDs" if [catch {set result [exec $loader -v usbserial vendor=0x$vid product=0x$pid]} err] { Log " Running \"$loader usbserial\" gave an error:\n $err" } else { @@ -923,25 +983,29 @@ } # end of proc {RemoveFromBindList} + proc {CheckSuccess} {devdir} { global config usb -set ifdir "[file tail $devdir]:1.0" +set ifdir [file tail [IfDir 0]] if {[string length $config(targetClass)] || [string length $config(Configuration)]} { set config(targetVendor) $usb(idVendor) set config(targetProduct) $usb(idProduct) } -Log "Checking success of mode switch for max. $config(checkSuccess) seconds ..." +Log "Check success of mode switch for max. $config(checkSuccess) seconds ..." for {set i 1} {$i <= $config(checkSuccess)} {incr i} { after 1000 if {![file isdirectory $devdir]} { - Log " Waiting for device file system ($i sec.) ..." + Log " Wait for device file system ($i sec.) ..." continue } else { - Log " Reading attributes ..." + Log " Read attributes ..." } + set ifdir [IfDir 0] + if {$ifdir == ""} {continue} + set ifdir [file tail $ifdir] if {![ReadUSBAttrs $devdir $ifdir]} { Log " Essential attributes are missing, continue wait ..." continue @@ -950,7 +1014,7 @@ if {![regexp $usb($ifdir/bInterfaceClass) $config(targetClass)]} {continue} } if [string length $config(Configuration)] { - if {$usb(bConfigurationValue) != $config(Configuration} {continue} + if {$usb(bConfigurationValue) != $config(Configuration)} {continue} } if {![regexp $usb(idVendor) $config(targetVendor)]} {continue} if {![regexp $usb(idProduct) $config(targetProduct)]} {continue} @@ -965,6 +1029,40 @@ } # end of proc {CheckSuccess} + +proc {IfDir} {iface} { + +global devdir +set allfiles [glob -nocomplain $devdir/*] +set files [glob -nocomplain $devdir/*.$iface] +if {[llength $files] == 0} { + return "" +} +set ifdir [lindex $files 0] +if {![file isdirectory $ifdir]} { + return "" +} +return $ifdir + +} +# end of proc {IfDir} + +proc {IfClass} {iface} { + +set ifdir [IfDir $iface] + +if {![file exists $ifdir/bInterfaceClass]} { + return -1 +} +set rc [open $ifdir/bInterfaceClass r] +set c [read $rc] +close $rc +return [string trimleft [string trim $c] 0] + +} +# end of proc {IfClass} + + proc {SysLog} {msg} { global flags @@ -986,6 +1084,38 @@ } # end of proc {SysLog} +proc {SetStorageDelay} {secs} { + +Log "Adjust delay for USB storage devices ..." +set attrib /sys/module/usb_storage/parameters/delay_use +if {![file exists $attrib]} { + Log "Error: could not find delay_use attribute" + return +} +if [catch {set ch [open $attrib r+]} err] { + Log "Error: could not access delay_use attribute: $err" + return +} +if {[read $ch] < $secs} { + seek $ch 0 start + puts -nonewline $ch $secs + Log " Delay set to $secs seconds\n" +} else { + Log " Current value is higher than $secs. Leave it alone\n" +} +close $ch + +} +# end of proc {SetStorageDelay} + +proc {CheckMBIM} {} { + +set kversion [exec uname -r] +if [file exists /lib/modules/$kversion/kernel/drivers/net/usb/cdc_mbim.ko] {return 1} +if [file exists /sys/bus/usb/drivers/cdc_mbim] {return 1} +return 0 + +} # The actual entry point diff -Nru usb-modeswitch-1.2.3+repack0/usb_modeswitch@.service usb-modeswitch-2.1.0+repack0/usb_modeswitch@.service --- usb-modeswitch-1.2.3+repack0/usb_modeswitch@.service 1970-01-01 00:00:00.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/usb_modeswitch@.service 2014-02-08 22:14:11.000000000 +0000 @@ -0,0 +1,6 @@ +[Unit] +Description=USB_ModeSwitch + +[Service] +Type=oneshot +ExecStart=/usr/sbin/usb_modeswitch_dispatcher --switch-systemd %I diff -Nru usb-modeswitch-1.2.3+repack0/usb_modeswitch_dispatcher.1 usb-modeswitch-2.1.0+repack0/usb_modeswitch_dispatcher.1 --- usb-modeswitch-1.2.3+repack0/usb_modeswitch_dispatcher.1 1970-01-01 00:00:00.000000000 +0000 +++ usb-modeswitch-2.1.0+repack0/usb_modeswitch_dispatcher.1 2014-02-08 22:14:11.000000000 +0000 @@ -0,0 +1,20 @@ +.TH "USB_MODESWITCH_DISPATCHER" "1" +.SH "NAME" +usb_modeswitch_dispatcher - wrapper for usb_modeswitch, not intended for direct invocation. +.SH "SYNOPSIS" +.PP +\fBusb_modeswitch_dispatcher\fR +.SH "DESCRIPTION" +.PP +usb_modeswitch_dispatcher will do detailed device checking and will subsequently +use the usb_modeswitch binary together with the selected device config file to +switch the mode of certain USB devices. +.PP +If no drivers are taking care of the device after the mode switch, the dispatcher +will try to load and bind the "option" serial driver, in order to make the device +useable. +.PP +This program is called by udev and is not supposed to be called directly +by the user. +.SH SEE ALSO +.BR usb_modeswitch(1).