The C Source, Patches and (shudder!) Bugs
Post Reply
linuxtardis
Posts: 9
Joined: 25 Dec 2019, 17:28

USB_ModeSwitch breaks with USB devices without interface 0

Post by linuxtardis » 25 Dec 2019, 18:33

Hi!

I have recently got a refurbished notebook with a builtin LTE modem - Sierra Wireless EM7305. However the modem has strange USB interface numbers, as the interface 0 exists only for configuration 1 and not for configuration 2:

Code: Select all

[    2.365878] usb 1-11: new high-speed USB device number 5 using xhci_hcd
[    2.516934] usb 1-11: config 1 has an invalid interface number: 8 but max is 3
[    2.516935] usb 1-11: config 1 has no interface number 1
[    2.517966] usb 1-11: config 2 has an invalid interface number: 12 but max is 1
[    2.517968] usb 1-11: config 2 has an invalid interface number: 13 but max is 1
[    2.517968] usb 1-11: config 2 has an invalid interface number: 13 but max is 1
[    2.517969] usb 1-11: config 2 has no interface number 0
[    2.517970] usb 1-11: config 2 has no interface number 1
[    2.519001] usb 1-11: New USB device found, idVendor=1199, idProduct=9041, bcdDevice= 0.06
[    2.519003] usb 1-11: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[    2.519004] usb 1-11: Product: EM7305
[    2.519005] usb 1-11: Manufacturer: Sierra Wireless, Incorporated

Code: Select all

Bus 001 Device 005: ID 1199:9041 Sierra Wireless, Inc. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x1199 Sierra Wireless, Inc.
  idProduct          0x9041 
  bcdDevice            0.06
  iManufacturer           1 Sierra Wireless, Incorporated
  iProduct                2 EM7305
  iSerial                 3 
  bNumConfigurations      2
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength          160
    bNumInterfaces          4
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xe0
      Self Powered
      Remote Wakeup
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass    255 Vendor Specific Subclass
      bInterfaceProtocol    255 Vendor Specific Protocol
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       0
      bNumEndpoints           3
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 
      iInterface              0 
      ** UNRECOGNIZED:  05 24 00 10 01
      ** UNRECOGNIZED:  05 24 01 00 00
      ** UNRECOGNIZED:  04 24 02 02
      ** UNRECOGNIZED:  05 24 06 00 00
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x000c  1x 12 bytes
        bInterval               9
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        3
      bAlternateSetting       0
      bNumEndpoints           3
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass      0 
      bInterfaceProtocol      0 
      iInterface              0 
      ** UNRECOGNIZED:  05 24 00 10 01
      ** UNRECOGNIZED:  05 24 01 00 00
      ** UNRECOGNIZED:  04 24 02 02
      ** UNRECOGNIZED:  05 24 06 00 00
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x85  EP 5 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x000c  1x 12 bytes
        bInterval               9
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x84  EP 4 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x03  EP 3 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        8
      bAlternateSetting       0
      bNumEndpoints           3
      bInterfaceClass       255 Vendor Specific Class
      bInterfaceSubClass    255 Vendor Specific Subclass
      bInterfaceProtocol    255 Vendor Specific Protocol
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x87  EP 7 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x000a  1x 10 bytes
        bInterval               9
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x86  EP 6 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x04  EP 4 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           95
    bNumInterfaces          2
    bConfigurationValue     2
    iConfiguration          0 
    bmAttributes         0xe0
      Self Powered
      Remote Wakeup
    MaxPower              500mA
    Interface Association:
      bLength                 8
      bDescriptorType        11
      bFirstInterface        12
      bInterfaceCount         2
      bFunctionClass          2 Communications
      bFunctionSubClass      14 
      bFunctionProtocol       0 
      iFunction               0 
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber       12
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         2 Communications
      bInterfaceSubClass     14 
      bInterfaceProtocol      0 
      iInterface              0 
      CDC Header:
        bcdCDC               1.10
      CDC Union:
        bMasterInterface        12
        bSlaveInterface         13 
      CDC MBIM:
        bcdMBIMVersion       1.00
        wMaxControlMessage   4096
        bNumberFilters       32
        bMaxFilterSize       128
        wMaxSegmentSize      1500
        bmNetworkCapabilities 0x20
          8-byte ntb input size
      UNRECOGNIZED CDC:  08 24 1c 00 01 40 dc 05
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               9
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber       13
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      2 
      iInterface              0 
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber       13
      bAlternateSetting       1
      bNumEndpoints           2
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      2 
      iInterface              0 
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               0
Device Qualifier (for other device speed):
  bLength                10
  bDescriptorType         6
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  bNumConfigurations      2
Device Status:     0x0000
  (Bus Powered)

After restart, the modem is in configuration 2; therefore the interface 0 will not be found. This breaks the dispatcher script - it refuses to do anything:

Code: Select all

USB_ModeSwitch log from Wed Dec 25 16:35:30 CET 2019

Use global config file: /etc/usb_modeswitch.conf
Raw parameters: {--switch-mode} {1-11}
Use top device dir /sys/bus/usb/devices/1-11
Check class of first interface ...
 No access to interface 0. Exit
I think that firmware update might resolve this problem. However there is also an Ubuntu bugreport about this issue, so I think that a general workaround would be welcome. (Ubuntu uses a C rewrite of the script, but apart from bugs, it seems to works similarly) https://bugs.launchpad.net/ubuntu/+sour ... ug/1676763

I have a rough clue how to fix it, but I don't know how to exactly express it in Tcl or C yet. If the dispatcher just needs to know some existing interface, the solution could be to just find the first interface available. I have this bash script as a proof-of-concept:

Code: Select all

FIRST_ITEM="$(ls /sys/bus/usb/devices | egrep "${KERNEL_NAME}:${CURRENT_CONFIG_NO}\\.*" | head -n 1)"
IFACE="$(echo "$FIRST_ITEM" | cut -d : -f 2 | cut -d . -f 2)"
I will try to further investigate the solution.

Best regards,

Jakub Vanek
Last edited by linuxtardis on 25 Dec 2019, 22:18, edited 1 time in total.

linuxtardis
Posts: 9
Joined: 25 Dec 2019, 17:28

Re: Dispatcher does not work on strange USB devices

Post by linuxtardis » 25 Dec 2019, 22:15

Yay! I have a local fix for the dispatcher Tcl script. Now the script successfully switches the modem if the kernel modules are unloaded manually (see below). However I have not tested it in other configurations, so tehre is a risk that I may have broken something.

Code: Select all

diff --git a/usb_modeswitch_dispatcher.tcl b/usb_modeswitch_dispatcher.tcl
index ee9da9e..899498a 100755
--- a/usb_modeswitch_dispatcher.tcl
+++ b/usb_modeswitch_dispatcher.tcl
@@ -134,17 +134,26 @@ if {![file isdirectory $devdir]} {
 }
 Log "Use top device dir $devdir"
 
-set iface 0
+set origCfgNo [FindConfigurationNumber $devdir]
+Log "Current configuration number: $origCfgNo"
+
+set ifdir [JustGetIfDir $devdir $device $origCfgNo]
+set iface [GetIface $ifdir]
+if {$ifdir != ""} {
+	Log " -> Found first interface $ifdir"
+} else {
+	Log " -> No interfaces found. Exit"
+	SafeExit
+}
+
 Log "Check class of first interface ..."
-set config(class) [IfClass 0 $devdir]
+set config(class) [IfClass $devdir $ifdir]
 if {$config(class) < 0} {
-	Log " No access to interface 0. Exit"
+	Log " No access to interface $iface. Exit"
 	SafeExit
 }
-Log " Interface 0 class is $config(class)."
+Log " Interface $iface class is $config(class)."
 
-set ifdir [file tail [IfDir $iface $devdir]]
-regexp {:([0-9]+\.[0-9]+)$} $ifdir d iface
 
 # Mapping of the short string identifiers (in the config
 # file names) to the long name used here
@@ -258,7 +267,7 @@ ParseDeviceConfig $flags(config)
 
 if [regexp -nocase {0x([0-9a-f]+)} $config(TargetClass) d tc] {
 	if {$tc == $config(class)} {
-		Log "Class of interface 0 matches target. Do nothing"
+		Log "Class of interface $iface matches target. Do nothing"
 		set report "ok:busdev"
 	}
 }
@@ -338,11 +347,13 @@ if {$report == ""} {
 # parameter were used; then we do check for success HERE
 
 if {$config(Configuration) != ""} {
-	set ifdir [regsub {(\d):\d+\.0} $ifdir "\\1:$config(Configuration).0"]
+	set newCfgNo $config(Configuration)
+} else {
+	set newCfgNo $origCfgNo
 }
 
 if [regexp {ok:busdev} $report] {
-	if [CheckSuccess $devdir] {
+	if [CheckSuccess $devdir $device $newCfgNo] {
 		Log "Mode switching was successful, found $usb(idVendor):$usb(idProduct) ($usb(manufacturer): $usb(product))"
 		SysLog "usb_modeswitch: switched to $usb(idVendor):$usb(idProduct) on [format %03d $usb(busnum)]/[format %03d $usb(devnum)]"
 	} else {
@@ -364,6 +375,11 @@ if [regexp {ok:busdev} $report] {
 	if {![file exists $devdir/idProduct]} {
 		after 1000
 	}
+	set ifdir [JustGetIfDir $devdir $device $newCfgNo]
+	if {$ifdir == ""} {
+		Log "Cannot find any interface under the new configuration number $cfgNo. Exit"
+		SafeExit
+	}
 	ReadUSBAttrs $devdir $ifdir
 }
 
@@ -377,6 +393,11 @@ if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
 }
 # wait for drivers to bind
 after 500
+set ifdir [JustGetIfDir $devdir $device $newCfgNo]
+if {$ifdir == ""} {
+	Log "Cannot find any interface under the new configuration number $cfgNo. Exit"
+	SafeExit
+}
 if {[llength [glob -nocomplain $devdir/$ifdir/ttyUSB*]] > 0} {
 	Log "Serial USB driver bound to interface 0\n will try to guess and symlink modem port on next connect"
 	AddToList link_list $usb(idVendor):$usb(idProduct)
@@ -805,7 +826,7 @@ close $lc
 # end of proc {AddToList}
 
 
-proc {CheckSuccess} {devdir} {
+proc {CheckSuccess} {devdir device newCfgNo} {
 
 global config usb flags
 
@@ -813,7 +834,6 @@ global config usb flags
 if {$usb(idVendor) == "1307" && $usb(idProduct) == "1169"} {
 	set devdir [string range $devdir 0 end-1]2
 }
-set ifdir [file tail [IfDir 0 $devdir]]
 if {[string length $config(TargetClass)] || [string length $config(Configuration)]} {
 	set config(TargetVendor) $usb(idVendor)
 	set config(TargetProduct) $usb(idProduct)
@@ -829,9 +849,8 @@ for {set i 1} {$i <= $config(CheckSuccess)} {incr i} {
 	} else {
 		Log " Read attributes ..."
 	}
-	set ifdir [IfDir 0 $devdir]
+	set ifdir [JustGetIfDir $devdir $device $newCfgNo]
 	if {$ifdir == ""} {continue}
-	set ifdir [file tail $ifdir]
 	if {![ReadUSBAttrs $devdir $ifdir]} {
 		Log " Essential attributes are missing, continue wait ..."
 		continue
@@ -878,31 +897,52 @@ if {$i > 20} {return 0} else {return 1}
 }
 # end of proc {CheckSuccess}
 
+proc {FindConfigurationNumber} {devdir} {
+set rc [open "$devdir/bConfigurationValue"]
+set value [read -nonewline $rc]
+close $rc
 
-proc {IfDir} {iface devdir} {
+return $value
+}
 
-set allfiles [glob -nocomplain $devdir/*]
-set files [glob -nocomplain $devdir/*.$iface]
-if {[llength $files] == 0} {
-	return ""
+proc {FindAllIfFullDirs} {devdir device cfgNo} {
+return [lsort [glob -nocomplain $devdir/$device:$cfgNo.*] ]
 }
-set ifdir [lindex $files 0]
-if {![file isdirectory $ifdir]} {
-	return ""
+# end of proc {FindAllIfDirs}
+
+proc {GetFirstIfFullDir} {ifFullDirs} {
+if {[llength $ifFullDirs] == 0} { return "" } else { return [lindex $ifFullDirs 0] }
 }
-return $ifdir
+# end of proc {GetFirstIfDir}
 
+proc {GetIfDir} {ifFullDir} {
+return [file tail $ifFullDir]
 }
-# end of proc {IfDir}
+# end of proc {GetIfDirName}
 
-proc {IfClass} {iface devdir} {
+proc {GetIface} {ifDir} {
+if [regexp {^.*:([0-9]+\.[0-9]+)$} $ifDir d iface] {
+	return $iface
+} else {
+	return ""
+}
+}
+# end of proc {GetIface}
+
+proc {JustGetIfDir} {devdir device cfgNo} {
+set ifFullDirs [FindAllIfFullDirs $devdir $device $cfgNo]
+set ifFullDir [GetFirstIfFullDir $ifFullDirs]
+set ifdir [GetIfDir $ifFullDir]
+return $ifdir
+}
+# end of proc {JustGetIfDir}
 
-set ifdir [IfDir $iface $devdir]
+proc {IfClass} {devdir ifdir} {
 
-if {![file exists $ifdir/bInterfaceClass]} {
+if {![file exists $devdir/$ifdir/bInterfaceClass]} {
 	return -1
 }
-set rc [open $ifdir/bInterfaceClass r]
+set rc [open $devdir/$ifdir/bInterfaceClass r]
 set c [read $rc]
 close $rc
 return [string trim $c]
@@ -1014,18 +1054,6 @@ if $flags(logging) {
 
 }
 
-proc {HasFF} {devdir} {
-
-set i 0
-while {[set dir [IfDir $i $devdir]] != ""} {
-	set c [exec cat $dir/bInterfaceClass]
-	if {$c == "ff"} {return 1}
-	incr i
-}
-return 0
-
-}
-
 
 # The actual entry point
 Main $argv $argc
However, the main usb_modeswitch executable also expects interface numbers in range [0, bNumInterfaces). This assumption does not hold on this modem, so the fix will have to go also there.

linuxtardis
Posts: 9
Joined: 25 Dec 2019, 17:28

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by linuxtardis » 25 Dec 2019, 22:55

The fixes to the main binary were a little easier to make :D

It turns out that libusb is aware of this and it provides correct interface numbers if the program accesses them through the descriptor.

Code: Select all

diff --git a/usb_modeswitch.c b/usb_modeswitch.c
index d0ce0e6..b008506 100644
--- a/usb_modeswitch.c
+++ b/usb_modeswitch.c
@@ -1480,13 +1480,14 @@ int detachDrivers()
 	libusb_get_active_config_descriptor(dev, &config);
 
 	for (i=0; i<config->bNumInterfaces; i++) {
-		ret = libusb_kernel_driver_active(devh, i);
+		int ifIndex = config->interface[i].altsetting[0].bInterfaceNumber;
+		ret = libusb_kernel_driver_active(devh, ifIndex);
 		if (ret < 0) {
-			SHOW_PROGRESS(output," Failed to check driver status for interface %d (error %d)\n Try to continue\n",i,ret);
+			SHOW_PROGRESS(output," Failed to check driver status for interface %d (error %d)\n Try to continue\n",ifIndex,ret);
 			continue;
 		}
 		if (ret) {
-			ret = libusb_detach_kernel_driver(devh, i);
+			ret = libusb_detach_kernel_driver(devh, ifIndex);
 			if (ret == LIBUSB_ERROR_NOT_SUPPORTED) {
 				fprintf(output," Can't do driver detaching on this platform.\n");
 				return 2;
@@ -1494,7 +1495,7 @@ int detachDrivers()
 			if (ret == 0) {
 				SHOW_PROGRESS(output," OK, driver detached\n");
 			} else {
-				SHOW_PROGRESS(output," Driver detach failed for interface %d (error %d).\n Try to continue\n",i,ret);
+				SHOW_PROGRESS(output," Driver detach failed for interface %d (error %d).\n Try to continue\n",ifIndex,ret);
 				continue;
 			}
 		}
When compiling the code, I got a compiler diagnostic that uncovered another bug - there is an uninitialized read from a variable (it is initialized to itself). I think the correct initial value should be zero.

Code: Select all

diff --git a/usb_modeswitch.c b/usb_modeswitch.c
index d0ce0e6..b008506 100644
--- a/usb_modeswitch.c
+++ b/usb_modeswitch.c
@@ -1586,7 +1587,7 @@ int checkSuccess()
 		 * description is read for syslog message
 		 */
 		// Wait counter passed on from previous loop
-		for (i=i; i < CheckSuccess; i++) {
+		for (i=0; i < CheckSuccess; i++) {
 			SHOW_PROGRESS(output," Search for target devices ...\n");
 			dev = search_devices(&newTargetCount, TargetVendor, TargetProductList,
 					TargetClass, 0, SEARCH_TARGET);

linuxtardis
Posts: 9
Joined: 25 Dec 2019, 17:28

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by linuxtardis » 25 Dec 2019, 22:58

Logs with the patches applied:

QMI -> MBIM:

Code: Select all

USB_ModeSwitch log from Wed Dec 25 21:57:09 CET 2019
Raw parameters: {--switch-mode} {1-11} 
Use global config file: /etc/usb_modeswitch.conf

Use global config file: /etc/usb_modeswitch.conf
Use top device dir /sys/bus/usb/devices/1-11
Current configuration number: 1
 -> Found first interface 1-11:1.0
Check class of first interface ...
 Interface 1.0 class is ff.

----------------
USB values from sysfs:
  manufacturer	Sierra Wireless, Incorporated
  product	EM7305
  serial	
----------------
Found packed config collection /usr/share/usb_modeswitch/configPack.tar.gz
ConfigList: pack/1199:9041 pack/
SCSI attributes not needed, move on
Check config: pack/1199:9041
! matched. Read config data
Extract config 1199:9041 from collection /usr/share/usb_modeswitch/configPack.tar.gz
Device may have an MBIM configuration, check driver ...
 driver for MBIM devices is available
Find MBIM configuration number ...
Command line:
usb_modeswitch -W -D  -b 1 -g 5 -v 1199 -p 9041 -f $flags(config)

Verbose debug output of usb_modeswitch and libusb follows
(Note that some USB errors are to be expected in the process)
--------------------------------

Read long config from command line

 * usb_modeswitch: handle USB devices with multiple modes
 * Version 2.6.0 (C) Josua Dietze 2017
 * Based on libusb1/libusbx

 ! PLEASE REPORT NEW CONFIGURATIONS !

DefaultVendor=  0x1199
DefaultProduct= 0x9041
Configuration=0x02
System integration mode enabled

Use given bus/device number: 001/005 ...
Look for default devices ...
 bus/device number matched
  found USB ID 1199:9041
   vendor ID matched
   product ID matched
 Found devices in default mode (1)
Get the current device configuration ...Logs 
Current configuration number is 1
Use interface number 0
 with class 255

USB description data (for identification)
-------------------------
Manufacturer: Sierra Wireless, Incorporated
     Product: EM7305
  Serial No.: 
-------------------------
Change configuration to 2 ...
Looking for active drivers ...
 OK, driver detached
 OK, driver detached
 OK, driver detached
 OK, driver detached
 Configuration was reset
 OK, configuration set
Get the current device configuration ...
The configuration was set successfully
ok:busdev
--------------------------------
(end of usb_modeswitch output)

Check success of mode switch for max. 20 seconds ...
 Read attributes ...
 All attributes matched
Mode switching was successful, found 1199:9041 (Sierra Wireless, Incorporated: EM7305)
Logger is /usr/bin/logger
Check for AVOID_RESET_QUIRK kernel attribute
 AVOID_RESET_QUIRK activated

All done, exit
MBIM -> QMI:

Code: Select all

USB_ModeSwitch log from Wed Dec 25 21:58:00 CET 2019
Raw parameters: {--switch-mode} {1-11} 
Use global config file: /etc/usb_modeswitch.conf

Use global config file: /etc/usb_modeswitch.conf
Use top device dir /sys/bus/usb/devices/1-11
Current configuration number: 2
 -> Found first interface 1-11:2.12
Check class of first interface ...
 Interface 2.12 class is 02.

----------------
USB values from sysfs:
  manufacturer	Sierra Wireless, Incorporated
  product	EM7305
  serial	
----------------
Found packed config collection /usr/share/usb_modeswitch/configPack.tar.gz
ConfigList: pack/1199:9041 pack/
SCSI attributes not needed, move on
Check config: pack/1199:9041
! matched. Read config data
Extract config 1199:9041 from collection /usr/share/usb_modeswitch/configPack.tar.gz
Command line:
usb_modeswitch -W -D  -b 1 -g 5 -v 1199 -p 9041 -f $flags(config)

Verbose debug output of usb_modeswitch and libusb follows
(Note that some USB errors are to be expected in the process)
--------------------------------

Read long config from command line

 * usb_modeswitch: handle USB devices with multiple modes
 * Version 2.6.0 (C) Josua Dietze 2017
 * Based on libusb1/libusbx

 ! PLEASE REPORT NEW CONFIGURATIONS !

DefaultVendor=  0x1199
DefaultProduct= 0x9041
Configuration=0x01
System integration mode enabled

Use given bus/device number: 001/005 ...
Look for default devices ...
 bus/device number matched
  found USB ID 1199:9041
   vendor ID matched
   product ID matched
 Found devices in default mode (1)
Get the current device configuration ...
Current configuration number is 2
Use interface number 12
 with class 2

USB description data (for identification)
-------------------------
Manufacturer: Sierra Wireless, Incorporated
     Product: EM7305
  Serial No.: 
-------------------------
Change configuration to 1 ...
Looking for active drivers ...
 OK, driver detached
 Configuration was reset
 OK, configuration set
Get the current device configuration ...
The configuration was set successfully
ok:busdev
--------------------------------
(end of usb_modeswitch output)

Check success of mode switch for max. 20 seconds ...
 Read attributes ...
 All attributes matched
Mode switching was successful, found 1199:9041 (Sierra Wireless, Incorporated: EM7305)
Logger is /usr/bin/logger
Serial USB driver bound to interface 0
 will try to guess and symlink modem port on next connect
Check for AVOID_RESET_QUIRK kernel attribute
 AVOID_RESET_QUIRK activated

All done, exit

linuxtardis
Posts: 9
Joined: 25 Dec 2019, 17:28

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by linuxtardis » 25 Dec 2019, 23:14

Hmmm, I'm now doing some experiments and I have found an interesting USB mode enumeration:

Code: Select all

kuba@linuxtardis-e734:~$ sudo qmicli -p -d /dev/cdc-wdm0 --dms-swi-get-usb-composition
[/dev/cdc-wdm0] Successfully retrieved USB compositions:
	    USB composition 1: HIP, DM, NMEA, AT, MDM1, MS
	    USB composition 6: DM, NMEA, AT, QMI
	    USB composition 7: DM, NMEA, AT, RMNET1, RMNET2, RMNET3
	    USB composition 8: DM, NMEA, AT, MBIM
	    USB composition 9: MBIM
	    USB composition 10: NMEA, MBIM
	    USB composition 11: DM, MBIM
	    USB composition 12: DM, NMEA, MBIM
	[*] USB composition 14: Dual configuration: USB composition 6 and USB composition 9
	    USB composition 19: Dual configuration: USB composition 7 and USB composition 9

LOM
Posts: 1404
Joined: 11 Jul 2012, 15:14
Location: Koh Samui, TH

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by LOM » 29 Dec 2019, 05:39

Josh will have to reply when he is back but from what I can see you generate lots of code in your patch for something that can probably be solved much easier.

Mobile Broadband dongles with install mode usually have the virtual cd storage function on interface 0 and it is to that interface we are sending the switch message. If the dongle has its virtual cd storage function on another interface then we can specify which interface to use in the device config file or as a switch on the cmdline.

Your Sierra card is not switched at all, it is a card with an mbim configuration so it is going through a test for presence of mbim drivers in your system and if they are there then usb_modeswitch will automatically select that configuration regardless of what the device config file or cmd line switches says. mbim always has the highest priority.
linux, when discovering your card for the first time, will set the default configuration to 2 which in this case also is the mbim configuration so usb_modeswitch has nothing to do, it will exist gracefully.
usb_modeswitch doesn't need to know anything about interface 0 present or not in order to handle this kind of device which doesn't use a switch message.

So don't check for interface 0 so early, simply move the check to just before we are about to send out the switch message.

linuxtardis
Posts: 9
Joined: 25 Dec 2019, 17:28

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by linuxtardis » 29 Dec 2019, 15:14

Hi LOM,

Thanks for the reply!
LOM wrote:Mobile Broadband dongles with install mode usually have the virtual cd storage function on interface 0 and it is to that interface we are sending the switch message. If the dongle has its virtual cd storage function on another interface then we can specify which interface to use in the device config file or as a switch on the cmdline.
Ah, then the check for presence of interface 0 makes sense. However, it think that specifying the interface is implemented only for usb_modeswitch.c, not for the dispatcher.

I think that then the solution for the bugreport in Ubuntu (where the C rewrite is buggy) is to simply fix its crash and let it exit gracefully on such a device (i.e. just like the current upstream dispatcher).

However I think the patches sent serve to add a new use case (see below).
LOM wrote:Your Sierra card is not switched at all, it is a card with an mbim configuration so it is going through a test for presence of mbim drivers in your system and if they are there then usb_modeswitch will automatically select that configuration regardless of what the device config file or cmd line switches says. mbim always has the highest priority.
linux, when discovering your card for the first time, will set the default configuration to 2 which in this case also is the mbim configuration so usb_modeswitch has nothing to do, it will exist gracefully.
I'd like to counter on this point. Yes, that is the case with the default configuration. However, I'd like to operate the modem in the QMI mode (as it has AT & GPS TTYs), so I have the following in my /etc/usb_modeswitch.conf:

Code: Select all

DisableSwitching=0
DisableMBIMGlobal=1
EnableLogging=1
HuaweiAltModeGlobal=0
I agree that this is probably unusual when compared to switching from VirtualCD mode to a modem mode. If this use case is not "officially" supported by usb_modeswitch, it is possible to work it around on these Qualcomm cards by permanently setting the USB composition via qmicli to the DM-NMEA-AT-QMI one.

However, bare usb_modeswitch.c supports configuring this as well (albeit temporarily). Also, the configPack.tar.gz entry (from Debian Buster) for this card contains reference to configuration 1, which is the QMI one. This is probably meant as a fallback for when the user disables the MBIM preference; however it implies that there is some form of support for this.

Code: Select all

# Sierra EM7305
Configuration=1
LOM wrote:usb_modeswitch doesn't need to know anything about interface 0 present or not in order to handle this kind of device which doesn't use a switch message.
I agree with that the device is switched by setting the configuration number on the whole device, so interface number is not needed in usb_modeswitch.c. However, the dispatcher needs to know at least one interface so that it can check if the [product,manufacturer,...] attributes were properly created.
LOM wrote:So don't check for interface 0 so early, simply move the check to just before we are about to send out the switch message.
I think that this would only hide the problem. For MBIM precedence, the reason would be correctly logged (mbim drivers are available and the device is in the correct mode). However when the MBIM precedence is turned off, this wouldn't work.

Best regards,

Jakub

EDIT: I hit submit too early, I added replies to the last two quotes.

linuxtardis
Posts: 9
Joined: 25 Dec 2019, 17:28

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by linuxtardis » 29 Dec 2019, 15:44

Another argument for this change is that usb_modeswitch.c gets the default interface in a similar way (it seems that it needs it for interface class query as well):

Code: Select all

	if (Interface == -1)
		Interface = active_config->interface[0].altsetting[0].bInterfaceNumber;
	SHOW_PROGRESS(output,"Use interface number %d\n", Interface);
I.e. it does not have interface 0 hardcoded, instead it looks up the first available interface and queries its interface number (which can have any value).

LOM
Posts: 1404
Joined: 11 Jul 2012, 15:14
Location: Koh Samui, TH

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by LOM » 29 Dec 2019, 19:21

linuxtardis wrote: I'd like to counter on this point. Yes, that is the case with the default configuration. However, I'd like to operate the modem in the QMI mode (as it has AT & GPS TTYs), so I have the following in my /etc/usb_modeswitch.conf:

Code: Select all

DisableSwitching=0
DisableMBIMGlobal=1
EnableLogging=1
HuaweiAltModeGlobal=0
I agree that this is probably unusual when compared to switching from VirtualCD mode to a modem mode. If this use case is not "officially" supported by usb_modeswitch, it is possible to work it around on these Qualcomm cards by permanently setting the USB composition via qmicli to the DM-NMEA-AT-QMI one.
Of course it is officially supported and that's the reason for the DisableMBIMGlobal setting, mbim has highest priority but the user is allowed to override that.
linuxtardis wrote: However, bare usb_modeswitch.c supports configuring this as well (albeit temporarily). Also, the configPack.tar.gz entry (from Debian Buster) for this card contains reference to configuration 1, which is the QMI one. This is probably meant as a fallback for when the user disables the MBIM preference; however it implies that there is some form of support for this.

Code: Select all

# Sierra EM7305
Configuration=1
It is there to make sure that a user with a system without mbim drivers gets the correct config selected.
linuxtardis wrote:
LOM wrote:usb_modeswitch doesn't need to know anything about interface 0 present or not in order to handle this kind of device which doesn't use a switch message.
I agree with that the device is switched by setting the configuration number on the whole device, so interface number is not needed in usb_modeswitch.c. However, the dispatcher needs to know at least one interface so that it can check if the [product,manufacturer,...] attributes were properly created.
I don't think the interface is needed in order to read those attributes but it struck me why we need to know the initial class of interface 0, there are devices which doesn't change vid:pid when morphing - they only change the interface composition. So for those device we can specify TargetClass=xx in the device config file in order to check that the device has morphed by comparing TargetClass with initial class.
Initial class is a storage class on interface 0 and if there is a storage class interface at all after morphing then it is not at interface 0, therefore it is important that we check interface 0 and bail out if it isn't available. :(

Sierra has really complicated things by not having an interface 0 in some of their compositions..

Josh
Site Admin
Posts: 6570
Joined: 03 Nov 2007, 00:30

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by Josh » 05 Jul 2020, 15:23

I have prepared a new release of usb_modeswitch.
Among some other fixes, I have changed the wrapper script to look for interfaces according to availability instead of fixed numbers. That means that the first interface may be ".2" or whatever the lowest available interface number is.

If you like you can test the changes. Just replace the respective files in the current source folder. There are no more changes.
Attachments
usb_modeswitch.c
(64.47 KiB) Downloaded 505 times
usb_modeswitch_dispatcher.tcl
(25.93 KiB) Downloaded 521 times

linuxtardis
Posts: 9
Joined: 25 Dec 2019, 17:28

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by linuxtardis » 06 Jul 2020, 14:01

Thank you very much!

I have tested the fix and now mode switching mostly works:
  • switching using usb_modeswitch directly works out-of-the-box in both directions, MBIM->QMI and QMI->MBIM
  • switching using usb_modeswitch_dispatcher.tcl works in both directions after a change in the IfDir function.
The problem in usb_modeswitch_dispatcher is that it does not find interfaces with number > 10 due to the single final [0-9] character class. This causes problems for the MBIM mode of this modem - there it exposes only interfaces 12 and 13. This also presents another problem - as the numbers go beyond 10, they cannot be sorted using alphanumeric sort because interfaces 1-9 are not prefixed with zero.

I have worked around the first problem by combining glob with a separate regexp filter:

Code: Select all

proc {IfDir} {iface devdir} {

set allfiles [glob -nocomplain $devdir/*]
set files [lsearch -all -inline -regexp $allfiles {^.*/[^/]*[0-9]+\.[0-9]+$}]
if {[llength $files] == 0} {
	return ""
}

set ifdir [lindex [lsort $files] $iface]
if {![string length $ifdir] || ![file isdirectory $ifdir]} {
	return ""
}
return $ifdir

}
# end of proc {IfDir}
After applying this fix, the dispatcher is able to switch the modem both ways. The second problem does not manifest here because the QMI mode exposes only interfaces 0, 2, 3 and 8. However, it seems that lsort -dictionary would solve this and an experiment in tclsh seems to confirm this:

Code: Select all

% lsort -dictionary { "/sys/bus/usb/devices/1-11/1-11:1.0" "/sys/bus/usb/devices/1-11/1-11:1.5" "/sys/bus/usb/devices/1-11/1-11:1.10" "/sys/bus/usb/devices/1-11/1-11:1.12" }
/sys/bus/usb/devices/1-11/1-11:1.0 /sys/bus/usb/devices/1-11/1-11:1.5 /sys/bus/usb/devices/1-11/1-11:1.10 /sys/bus/usb/devices/1-11/1-11:1.12

Josh
Site Admin
Posts: 6570
Joined: 03 Nov 2007, 00:30

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by Josh » 09 Jul 2020, 17:34

Exellent!

I'll include both fixes in the release version. I wouldn't have been able to test this as in my - rather extended - modem collection there is none with such an 'extreme' setup ...

Josh
Site Admin
Posts: 6570
Joined: 03 Nov 2007, 00:30

Re: USB_ModeSwitch breaks with USB devices without interface 0

Post by Josh » 09 Jul 2020, 21:39

Some testing revealed that in the default "jimsh", the -dictionary parameter is not implemented for lsort.

I added a work-around where I first sort the list of files with one digit for the interface, then append the sorted list of ifaces with two digits and leave it at that ...

Post Reply