Wish List (generally ignored ;-), Brainstorming
Post Reply
rjenkins
Posts: 5
Joined: 04 Nov 2011, 18:45

Device targeted mode switching

Post by rjenkins » 04 Nov 2011, 18:57

I'm working on a device that requires supporting and connecting multiple modems simultaneously, and I noticed that usb_modeswitch currently receives info about exactly what device needs mode switching from udev, but then simply switches the last device it finds with the same USB IDs. The result of this is if I use three of the same type of modem, when udev starts it will mode switch one of the modems three times. Not exactly the desired effect.

I think a way to target a single device, maybe using its devnum, would be useful. Especially for manufacturers like Huawei who use the same USB IDs for many of their modems.

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

Post by Josh » 04 Nov 2011, 23:13

The usb_modeswitch "framework" will do fine if modems are inserted one at a time. But you are right, a simultaneous addition of similar devices may not be handled very well.

Anyway, this application case has not come up until now ...

I will give it some thought - it should be possible to retrieve and pass the bus/dev address from wrapper to binary.


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

Post by Josh » 11 Nov 2011, 01:48

I have done some work and changed the handling to use the bus/device number. No more ambiguity.

If you want, check out the test versions of data and program package:

http://www.draisberghof.de/usb_modeswit ... ta.tar.bz2
http://www.draisberghof.de/usb_modeswit ... xx.tar.bz2

I tested with two Huaweis (same default ID) on a powered hub that I inserted with the modems powered up already. That's as close as I can simulate your setup ...


rjenkins
Posts: 5
Joined: 04 Nov 2011, 18:45

Post by rjenkins » 11 Nov 2011, 19:45

Installed the beta on our device and initial testing looks good. I'll do some more testing with it, but this seems to handle multiple modem mode switches, and resolves the issue I was having with modems attached to USB hubs chained 2 deep.

I did see another issue with multiple modems, but this has to do with creating the gsmmodem symlinks. Because usb_modeswitch checks if the symlink exists, and then passes the name back to udev, if udev then runs usb_modeswitch again it could return the same symlink because it hasn't been created yet for the other device. My initial though on how to fix this was to have usb_modeswitch_dispatcher create the symlink itself, but the jimsh version I have doesn't include the 'file link' command. Instead I just created a placeholder regular file where the symlink would be created, so then usb_modeswitch would know if that symlink is taken, and when udev got around to installing the symlink, it would just replace the regular file with the correct symlink.

Code: Select all

@@ -746,6 +764,9 @@
 }
 if {$idx == 256} {return ""}
 
+set placeholder [open /dev/$symlinkName w]
+close $placeholder
+
 Log "Return symlink name \"$symlinkName\" and exit"
 return $symlinkName
 
One other issue I noticed was with a modem I have at work: Sierra Wireless Aircard 250U. Now, I know this device isn't mode switched, but I use usb_modeswitch to detect which ttyUSB* port to attempt PPP on, using the gsmmodem symlinks. However, with the 250U, it has 4 ttyUSB* ports on the same USB interface 0.

Code: Select all

# ls /sys/bus/usb/devices/1-2.2.2:1.0/
bAlternateSetting     ep_05                 power
bInterfaceClass       ep_08                 subsystem
bInterfaceNumber      ep_81                 supports_autosuspend
bInterfaceProtocol    ep_82                 ttyUSB0
bInterfaceSubClass    ep_84                 ttyUSB1
bNumEndpoints         ep_85                 ttyUSB2
driver                ep_88                 ttyUSB3
ep_02                 interface             uevent
ep_04                 modalias
In this case, the proper tty to use is ttyUSB0. However, the symlink creation in usb_modeswitch would assign the symlinks haphazardly (different ttys and different amount on each power cycle). The different number of symlinks created for this device is probably explained by the previous issue I noted, but when that is resolved then it would just create a symlink for every tty in that interface instead.

I wrote some code to check if there are any lower enumerated ttys in the same interface:

Code: Select all

@@ -727,6 +727,24 @@
       }
    }
 }
+
+# Also, some devices have multiple TTYs assocated with a single
+# interface. Check for any lower indexed TTYs, and if found
+# don't return any name. ttyUSB0, of course, needn't worry.
+
+if { $rightPort && ![string equal $myPort "ttyUSB0"] } {
+    Log "\nLooking for lower TTYs in the same interface"
+    set ifDir $ifRoot.$ifNum
+    set otherPorts [glob -nocomplain $ifDir/ttyUSB\[0-9\]*]
+    foreach thisPort $otherPorts {
+        if { [string compare $myPort [file tail $thisPort]] > 0 } {
+            Log "\n--> found a port below me\n"
+            set rightPort 0
+            break
+        }
+    }
+}
+
 if {$rightPort == 0} {
    Log "Return empty name and exit"
    return ""
Probably not super useful until there's a mode switched modem that exhibits the same interface, but I thought I'd throw it out there.

Thanks for the help. The less I have to patch the software packages I port to our device, the easier it is to upgrade and maintain them later.

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

Post by Josh » 11 Nov 2011, 21:17

rjenkins wrote:Instead I just created a placeholder regular file where the symlink would be created, so then usb_modeswitch would know if that symlink is taken, and when udev got around to installing the symlink, it would just replace the regular file with the correct symlink.
That's clever - if it works all the time. I will test on various (some very old) systems.
I'd rather leave the handling of symlinks to udev - no worries about the unplugging process.
rjenkins wrote:However, with the 250U, it has 4 ttyUSB* ports on the same USB interface 0.
Very peculiar, indeed. Didn't know this was even possible ...

Anyway, I think there are some improvements possible for your code. It might not be neccessary to check all other interface ttys. Just check if there is one that's lower than your's or not.
Also, "ifDir" is set already when arriving at that point.

Code: Select all

@@ -727,6 +727,24 @@
       }
    }
 }
+
+# Also, some devices have multiple TTYs assocated with a single
+# interface. Check for any lower indexed TTYs, and if found
+# don't return any name. ttyUSB0, of course, needn't worry.
+
+if { $rightPort } {
+    regexp {.*?([0-9]+)$} $myPort d portNum
+    if {$portNum > 0} {
+        Log "\nLooking for lower TTYs in the same interface"
+        if [file exists $ifDir/ttyUSB[incr $portNum -1]] {
+            Log "\n--> found a port below me\n"
+            set rightPort 0
+        }
+    }
+}
+
 if {$rightPort == 0} {
    Log "Return empty name and exit"
    return ""
Untested, but I think you get the idea.
rjenkins wrote:Thanks for the help. The less I have to patch the software packages I port to our device, the easier it is to upgrade and maintain them later.
To be honest, that ambiguity issue was one of the things I knew I would not get through with forever.
So I'm actually grateful for the little push from the real world.


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

Post by Josh » 11 Nov 2011, 21:52

One more thing regarding symlink numbering: I just tested the approach to let udev put a number to the link. In the "rules" file:

Code: Select all

... SYMLINK="%c%n"
Then make the script always return just "gsmmodem", and you get the number attached according to the port number.

Example (three modems): "gsmmodem0", "gsmmodem4", "gsmmodem8".

Not very neat, but guaranteed to prevent races.


rjenkins
Posts: 5
Joined: 04 Nov 2011, 18:45

Post by rjenkins » 12 Nov 2011, 00:36

Josh wrote:Anyway, I think there are some improvements possible for your code. It might not be neccessary to check all other interface ttys. Just check if there is one that's lower than your's or not.
That's clever, but it does assume that the TTY names allocated to the device will be sequential. I'm not sure if that's a good assumption to make, because it's possible to connect and remove USB devices and end up with non-sequential TTY device names on a multiport USB serial converter. Example:

Connect two 1-port USB serial adapters, which get enumerated ttyUSB0 & ttyUSB1. Remove the USB serial adapter associated with ttyUSB0, now plug in a 4-port USB serial adapter, which will get enumerated ttyUSB0 & ttyUSB2-4. If you used modems instead of serial adapters then you'd get two symlinks for the 4-port USB modem: ttyUSB0 and ttyUSB2, because they both don't have an interface one index beneath them on the same device.

That said, the 250U does seem to require sequential TTY names, as when I did the above on my workstation it was enumerated ttyUSB2-5 and ttyUSB0 was left un-allocated. I'm not sure if that's a requirement of the sierra driver or the USB subsystem, but it seems safer to check all the other TTYs on the same interface just in case they get allocated differently on some device.
Josh wrote:One more thing regarding symlink numbering: I just tested the approach to let udev put a number to the link. In the "rules" file:

Code: Select all

... SYMLINK="%c%n"
Then make the script always return just "gsmmodem", and you get the number attached according to the port number.
Thanks for that, that will also make it much easier to tell if ttyUSBX is a proper PPP serial port if I only have to check for gsmmodemX, instead of readlink on every gsmmodem link in /dev.

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

Post by Josh » 12 Nov 2011, 02:33

Aw, you have a point there - the ports may not be sequential.

I just stubbornly refuse to accept that a loop is required for that task - can't help it ...
How about this ():

Code: Select all

if { $rightPort } { 
    Log "\nLooking for lower TTYs in the same interface" 
    set lowPort [lindex [lsort -dictionary [glob $ifDir/ttyUSB\[0-9\]*]] 0]
    regexp {ttyUSB([0-9]+)ttyUSB([0-9]+)} $lowPort$myPort d low my
    if { $low < $my } {
        Log "\n--> found a port below me\n" 
        set rightPort 0 
    } 
}
BTW, string compare (like in your first version) may run into trouble with higher tty numbers (e.g. above 9) because it sees ttyUSB10 below ttyUSB9 (lexicographical).

lsort does it better with "-dictionary" - unfortunately jimsh doesn't seem to have it.
Oh well, back to square one.


Post Reply