Miniupnpd: update to 2.0
[tomato.git] / release / src / router / usbmodeswitch / usb_modeswitch.tcl
blob3e590fdb03d26004ba2d6edeaf758147d09c70e7
1 #!/usr/bin/tclsh
3 # Wrapper (tcl) for usb_modeswitch, called from
4 # /lib/udev/rules.d/40-usb_modeswitch.rules
5 # (part of data pack "usb-modeswitch-data") via
6 # /lib/udev/usb_modeswitch
8 # Does ID check on newly discovered USB devices and calls
9 # the mode switching program with the matching parameter
10 # file from /usr/share/usb_modeswitch
12 # Part of usb-modeswitch-2.4.0 package
13 # (C) Josua Dietze 2009-2016
15 set arg0 [lindex $argv 0]
16 if [regexp {\.tcl$} $arg0] {
17 if [file exists $arg0] {
18 set argv [lrange $argv 1 end]
19 source $arg0
20 exit
24 # Setting of these switches is done in the global config
25 # file (/etc/usb_modeswitch.conf) if available
27 set flags(logging) 1
28 set flags(noswitching) 0
29 set flags(stordelay) 0
30 set flags(logwrite) 0
31 # also settable in device config files
32 set flags(nombim) 0
34 # Execution starts at file bottom
36 proc {Main} {argv argc} {
38 global scsi usb config match device flags setup devdir loginit
40 set flags(config) ""
41 Log "[ParseGlobalConfig]"
43 # The facility to add a symbolic link pointing to the
44 # ttyUSB port which provides interrupt transfer, i.e.
45 # the port to connect through.
46 # Will check for interrupt endpoint in ttyUSB port (lowest if
47 # there is more than one); if found, return "gsmmodem[n]" name
48 # to udev for symlink creation
50 # This is run once for every port of LISTED devices by
51 # a udev rule
53 if {[lindex $argv 0] == "--symlink-name"} {
54 puts -nonewline [SymLinkName [lindex $argv 1]]
55 SafeExit
58 if {[lindex $argv 0] == "--switch-systemd"} {
59 set argList [split [lindex $argv 1] _]
60 Log "\nStarted via systemd"
61 } else {
62 if {[lindex $argv 0] == "--switch-upstart"} {
63 Log "\nStarted via upstart"
65 set argList [split [lindex $argv 1] /]
67 if [string length [lindex $argList 1]] {
68 set device [lindex $argList 1]
69 } else {
70 set device "noname"
72 if {$flags(stordelay) > 0} {
73 SetStorageDelay $flags(stordelay)
76 Log "Raw args from udev: [lindex $argv 1]\n"
78 if {$device == "noname"} {
79 Log "\nNo data from udev. Exit"
80 SafeExit
83 if {![regexp -- {--switch-} [lindex $argv 0]]} {
84 Log "\nNo command given. Exit"
85 SafeExit
88 set setup(dbdir) /usr/share/usb_modeswitch
89 set setup(dbdir_etc) /etc/usb_modeswitch.d
92 if {![file exists $setup(dbdir)] && ![file exists $setup(dbdir_etc)]} {
93 Log "\nError: no config database found in /usr/share or /etc. Exit"
94 SafeExit
96 set bindir /usr/sbin
98 set devList1 {}
99 set devList2 {}
102 # arg 0: the bus id for the device (udev: %b), often ommitted
103 # arg 1: the "kernel name" for the device (udev: %k)
105 # Used to determine the top directory for the device in sysfs
107 set ifChk 0
108 if {[string length [lindex $argList 0]] == 0} {
109 if {[string length [lindex $argList 1]] == 0} {
110 Log "No device number values given from udev! Exit"
111 SafeExit
112 } else {
113 if {![regexp {(.*?):} [lindex $argList 1] d dev_top]} {
114 if {![regexp {([0-9]+-[0-9]+\.?[0-9]*.*)} [lindex $argList 1] d dev_top]} {
115 Log "Could not determine device dir from udev values! Exit"
116 SafeExit
120 } else {
121 set dev_top [lindex $argList 0]
122 regexp {(.*?):} $dev_top d dev_top
125 set devdir /sys/bus/usb/devices/$dev_top
126 if {![file isdirectory $devdir]} {
127 Log "Top device directory not found ($devdir)! Exit"
128 SafeExit
130 Log "Use top device dir $devdir"
132 set iface 0
133 Log "Check class of first interface ..."
134 set config(class) [IfClass 0 $devdir]
135 if {$config(class) < 0} {
136 Log " No access to interface 0. Exit"
137 SafeExit
139 Log " Interface 0 class is $config(class)."
141 set ifdir [file tail [IfDir $iface $devdir]]
142 regexp {:([0-9]+\.[0-9]+)$} $ifdir d iface
144 set flags(logwrite) 1
146 # Mapping of the short string identifiers (in the config
147 # file names) to the long name used here
149 # If we need them it's a snap to add new attributes here!
151 set match(sVe) scsi(vendor)
152 set match(sMo) scsi(model)
153 set match(sRe) scsi(rev)
154 set match(uMa) usb(manufacturer)
155 set match(uPr) usb(product)
156 set match(uSe) usb(serial)
159 # Now reading the USB attributes
160 if {![ReadUSBAttrs $devdir]} {
161 Log "USB attributes not found in sysfs tree. Exit"
162 SafeExit
164 set config(vendor) $usb(idVendor)
165 set config(product) $usb(idProduct)
168 if $flags(logging) {
169 Log "\n----------------\nUSB values from sysfs:"
170 foreach attr {manufacturer product serial} {
171 Log " $attr\t$usb($attr)"
173 Log "----------------"
176 if $flags(noswitching) {
177 SysLog "usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)"
178 Log "\nSwitching globally disabled. Exit"
179 SafeExit
182 if {$usb(bNumConfigurations) == "1"} {
183 set configParam "-u -1"
184 Log "bNumConfigurations is 1 - don't check for active configuration"
185 } else {
186 set configParam ""
189 # Check (and switch) for operating system if Huawei device present
191 set flags(os) "linux"
192 if {$usb(idVendor) == "12d1" && [regexp -nocase {android} [exec cat /proc/version]]} {
193 set flags(os) "android"
195 if {$flags(os) == "android"} {
196 set configList [ConfigGet conflist $usb(idVendor):#android]
197 } else {
198 set configList [ConfigGet conflist $usb(idVendor):$usb(idProduct)]
201 if {[llength $configList] == 0} {
202 Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exit"
203 SafeExit
205 Log "ConfigList: $configList"
207 # Check if there is more than one config file for this USB ID,
208 # which would make an attribute test necessary. If so, check if
209 # SCSI values are needed
211 set scsiNeeded 0
212 if {[llength $configList] > 1} {
213 if [regexp {:s} $configList] {
214 set scsiNeeded 1
217 if $scsiNeeded {
218 if [ReadSCSIAttrs $devdir:$iface] {
219 Log "----------------\nSCSI values from sysfs:"
220 foreach attr {vendor model rev} {
221 Log " $attr\t$scsi($attr)"
223 Log "----------------"
224 } else {
225 Log "Could not get SCSI attributes, exclude devices with SCSI match"
227 } else {
228 Log "SCSI attributes not needed, move on"
231 # General wait - some devices need this
232 after 500
234 # Now check for a matching config file. Matching is done
235 # by MatchDevice
237 set report {}
238 foreach mconfig $configList {
240 # skipping installer leftovers like "*.rpmnew"
241 if [regexp {\.(dpkg|rpm)} $mconfig] {continue}
243 Log "Check config: $mconfig"
244 if [MatchDevice $mconfig] {
245 Log "! matched. Read config data"
246 # set flags(config) $mconfig
247 if [string length $usb(busnum)] {
248 set busParam "-b [string trimleft $usb(busnum) 0]"
249 set devParam "-g [string trimleft $usb(devnum) 0]"
250 } else {
251 set busParam ""
252 set devParam ""
254 set flags(config) [ConfigGet conffile $mconfig]
255 ParseDeviceConfig $flags(config)
256 if [regexp -nocase {/[0-9a-f]+:#} $flags(config)] {
257 Log "Note: Using generic manufacturer configuration for \"$flags(os)\""
259 if $flags(nombim) {
260 set config(NoMBIMCheck) 1
262 if {$config(WaitBefore) != ""} {
263 Log "Delay time of $config(WaitBefore) seconds"
264 append config(WaitBefore) "000"
265 after $config(WaitBefore)
266 Log " wait is over, start mode switch"
268 if {$config(NoMBIMCheck)==0 && $usb(bNumConfigurations) > 1} {
269 Log "Device may have an MBIM configuration, check driver ..."
270 if [CheckMBIM] {
271 Log " driver for MBIM devices is available"
272 Log "Find MBIM configuration number ..."
273 if [catch {set cfgno [exec /usr/sbin/usb_modeswitch -j -Q $busParam $devParam -v $usb(idVendor) -p $usb(idProduct)]} err] {
274 Log "Error when trying to find MBIM configuration, switch to legacy modem mode"
275 } else {
276 set cfgno [string trim $cfgno]
277 if {$cfgno > 0} {
278 set config(Configuration) $cfgno
279 set flags(config) "Configuration=$cfgno"
280 } else {
281 Log " No MBIM configuration found, switch to legacy modem mode"
284 } else {
285 Log " no MBIM driver found, switch to legacy modem mode"
288 if [PantechAutoSwitch] {
289 Log "Waiting for Pantech auto-modeswitch"
290 set report "ok:busdev"
291 break
293 if {$config(Configuration) == 0} {
294 Log "Config file contains dummy method, do nothing. Exit"
295 SafeExit
297 UnbindDriver $devdir $ifdir
298 # Now we are actually switching
299 if $flags(logging) {
300 Log "Command to be run:\nusb_modeswitch -W -D $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f \$flags(config)"
301 set report [exec /usr/sbin/usb_modeswitch -W -D $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$flags(config)" 2>@1]
302 Log "\nVerbose debug output of usb_modeswitch and libusb follows"
303 Log "(Note that some USB errors are to be expected in the process)"
304 Log "--------------------------------"
305 Log $report
306 Log "--------------------------------"
307 Log "(end of usb_modeswitch output)\n"
308 } else {
309 set report [exec /usr/sbin/usb_modeswitch -Q -D $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$flags(config)" 2>@1]
311 break
312 } else {
313 Log "* no match, don't use this config"
317 # Switching is complete; success checking was either
318 # done by usb_modeswitch and logged via syslog OR bus/dev
319 # parameter were used; then we do check for success HERE
321 if {$config(Configuration) != ""} {
322 set ifdir [regsub {(\d):\d+\.0} $ifdir "\\1:$config(Configuration).0"]
325 if [regexp {ok:busdev} $report] {
326 if [CheckSuccess $devdir] {
327 Log "Mode switching was successful, found $usb(idVendor):$usb(idProduct) ($usb(manufacturer): $usb(product))"
328 SysLog "usb_modeswitch: switched to $usb(idVendor):$usb(idProduct) on [format %03d $usb(busnum)]/[format %03d $usb(devnum)]"
329 } else {
330 Log "\nTarget config not matching - current values are"
331 LogAttributes
332 Log "\nMode switching may have failed. Exit"
333 SafeExit
335 } else {
336 if {![file isdirectory $devdir]} {
337 Log "Device directory in sysfs is gone! Something went wrong, abort"
338 SafeExit
340 if {![regexp {ok:} $report]} {
341 Log "\nCore program reported switching failure. Exit"
342 SafeExit
344 # Give the device another second if it's not fully back yet
345 if {![file exists $devdir/idProduct]} {
346 after 1000
348 ReadUSBAttrs $devdir $ifdir
351 # driver binding removed !!
353 if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
354 if {![regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct)]} {
355 Log "No target vendor/product ID found or given, can't continue. Abort"
356 SafeExit
359 # wait for drivers to bind
360 after 500
361 if {[llength [glob -nocomplain $devdir/$ifdir/ttyUSB*]] > 0} {
362 Log "Serial USB driver bound to interface 0\n will try to guess and symlink modem port on next connect"
363 AddToList link_list $usb(idVendor):$usb(idProduct)
366 # In newer kernels there is a switch to avoid the use of a device
367 # reset (e.g. from usb-storage) which would possibly switch back
368 # a mode-switching device to initial mode
369 if [regexp {ok:} $report] {
370 Log "Check for AVOID_RESET_QUIRK kernel attribute"
371 if [file exists $devdir/avoid_reset_quirk] {
372 if [catch {exec echo "1" >$devdir/avoid_reset_quirk 2>/dev/null} err] {
373 Log " Error setting the attribute: $err"
374 } else {
375 Log " AVOID_RESET_QUIRK activated"
377 } else {
378 Log " not present in this kernel"
382 Log "\nAll done, exit\n"
383 SafeExit
386 # end of proc {Main}
389 proc {ReadSCSIAttrs} {topdir} {
391 global scsi
392 set counter 0
393 set sysdir $topdir
394 Log "Check storage tree in sysfs ..."
395 while {$counter < 20} {
396 Log " loop $counter/20"
397 if {![file isdirectory $sysdir]} {
398 # Device is gone. Unplugged? Switched by kernel?
399 Log " sysfs device tree is gone; abort SCSI value check"
400 return 0
402 # Searching the storage/SCSI tree; might take a while
403 if {[set dirList [glob -nocomplain $topdir/host*]] != ""} {
404 set sysdir [lindex $dirList 0]
405 if {[set dirList [glob -nocomplain $sysdir/target*]] != ""} {
406 set sysdir [lindex $dirList 0]
407 regexp {.*target(.*)} $sysdir d subdir
408 if {[set dirList [glob -nocomplain $sysdir/$subdir*]] != ""} {
409 set sysdir [lindex $dirList 0]
410 if [file exists $sysdir/vendor] {
411 Log " Storage tree is ready"
412 break
417 after 500
418 incr counter
420 if {$counter == 20} {
421 Log "SCSI tree not found; you may want to check if this path/file exists:"
422 Log "$sysdir/vendor\n"
423 return 0
426 Log "Read SCSI values ..."
427 foreach attr {vendor model rev} {
428 if [file exists $sysdir/$attr] {
429 set rc [open $sysdir/$attr r]
430 set scsi($attr) [read -nonewline $rc]
431 close $rc
432 } else {
433 set scsi($attr) ""
434 Log "Warning: SCSI attribute \"$attr\" not found."
437 return 1
440 # end of proc {ReadSCSIAttrs}
443 proc {ReadUSBAttrs} {dir args} {
445 global usb
447 set attrList {idVendor idProduct bConfigurationValue manufacturer product serial devnum busnum bNumConfigurations}
448 set mandatoryList {idVendor idProduct bNumConfigurations}
449 set result 1
450 if {$args != ""} {
451 lappend attrList "$args/bInterfaceClass"
452 lappend mandatoryList "$args/bInterfaceClass"
454 foreach attr $attrList {
455 if [file exists $dir/$attr] {
456 set rc [open $dir/$attr r]
457 set usb($attr) [string trim [read -nonewline $rc]]
458 close $rc
459 } else {
460 set usb($attr) ""
461 if {[lsearch $mandatoryList $attr] > -1} {
462 set result 0
464 if {$attr == "serial"} {continue}
465 Log " Warning: USB attribute \"$attr\" not found"
468 return $result
471 # end of proc {ReadUSBAttrs}
474 proc {MatchDevice} {config} {
476 global scsi usb match
478 set devinfo [file tail $config]
479 set infoList [split $devinfo :]
480 set stringList [lrange $infoList 2 end]
481 if {[llength $stringList] == 0} {return 1}
483 foreach teststring $stringList {
484 if {$teststring == "?"} {return 0}
485 set tokenList [split $teststring =]
486 set id [lindex $tokenList 0]
487 set matchstring [lindex $tokenList 1]
488 set blankstring ""
489 regsub -all {_} $matchstring { } blankstring
490 Log "match $match($id)"
491 Log " string1 (exact): $matchstring"
492 Log " string2 (blanks): $blankstring"
493 Log " device string: [set $match($id)]"
494 if {!([string match *$matchstring* [set $match($id)]] || [string match *$blankstring* [set $match($id)]])} {
495 return 0
498 return 1
501 # end of proc {MatchDevice}
504 proc {ParseGlobalConfig} {} {
506 global flags
507 set configFile ""
508 set places [list /etc/usb_modeswitch.conf /etc/sysconfig/usb_modeswitch /etc/default/usb_modeswitch]
509 foreach cfg $places {
510 if [file exists $cfg] {
511 set configFile $cfg
512 break
515 if {$configFile == ""} {return}
517 set rc [open $configFile r]
518 while {![eof $rc]} {
519 gets $rc line
520 if [regexp {^#} [string trim $line]] {continue}
521 if [regexp {DisableMBIMGlobal\s*=\s*([^\s]+)} $line d val] {
522 if [regexp -nocase {1|yes|true} $val] {
523 set flags(nombim) 1
524 } else {
525 set flags(nombim) 0
528 if [regexp {DisableSwitching\s*=\s*([^\s]+)} $line d val] {
529 if [regexp -nocase {1|yes|true} $val] {
530 set flags(noswitching) 1
533 if [regexp {EnableLogging\s*=\s*([^\s]+)} $line d val] {
534 if [regexp -nocase {1|yes|true} $val] {
535 set flags(logging) 1
536 } else {
537 set flags(logging) 0
540 if [regexp {SetStorageDelay\s*=\s*([^\s]+)} $line d val] {
541 if [regexp {\d+} $val] {
542 set flags(stordelay) $val
547 return "Use global config file: $configFile"
550 # end of proc {ParseGlobalConfig}
553 proc ParseDeviceConfig {cfg} {
555 global config
556 set config(WaitBefore) ""
557 set config(TargetVendor) ""
558 set config(TargetProduct) ""
559 set config(TargetClass) ""
560 set config(Configuration) ""
561 set config(NoMBIMCheck) 0
562 set config(PantechMode) 0
563 set config(CheckSuccess) 20
565 foreach pname [lsort [array names config]] {
566 if [regexp -line "^\[^# \]*?$pname.*?= *(0x(\\w+)|\"(\[0-9a-fA-F,\]+)\"|(\[0-9\]+)) *\$" $cfg d config($pname)] {
567 # Log "config: $pname set to $config($pname)"
571 set config(WaitBefore) [string trimleft $config(WaitBefore) 0]
574 # end of proc {ParseDeviceConfig}
577 proc ConfigGet {command config} {
579 global setup usb flags
581 switch $command {
583 conflist {
584 # Unpackaged configs first; sorting is essential for priority
585 set configList [lsort -decreasing [glob -nocomplain $setup(dbdir_etc)/$config*]]
586 set configList [concat $configList [lsort -decreasing [glob -nocomplain $setup(dbdir)/$config*]]]
587 eval lappend configList [glob -nocomplain $setup(dbdir)/$usb(idVendor):#$flags(os)]
588 if [file exists $setup(dbdir)/configPack.tar.gz] {
589 Log "Found packed config collection $setup(dbdir)/configPack.tar.gz"
590 if [catch {set packedList [exec tar -tzf $setup(dbdir)/configPack.tar.gz 2>/dev/null]} err] {
591 Log "Error: problem opening config package; tar returned\n $err"
592 return {}
594 set packedList [split $packedList \n]
595 set packedConfigList [lsort -decreasing [lsearch -glob -all -inline $packedList $config*]]
596 lappend packedConfigList [lsearch -inline $packedList $usb(idVendor):#$flags(os)]
597 # Now add packaged configs with a mark, again sorted for priority
598 foreach packedConfig $packedConfigList {
599 lappend configList "pack/$packedConfig"
602 return $configList
604 conffile {
605 if [regexp {^pack/} $config] {
606 set config [regsub {pack/} $config {}]
607 Log "Extract config $config from collection $setup(dbdir)/configPack.tar.gz"
608 set configContent [exec tar -xzOf $setup(dbdir)/configPack.tar.gz $config 2>/dev/null]
609 } else {
610 if [regexp [list $setup(dbdir_etc)] $config] {
611 Log "Use config file from override folder $setup(dbdir_etc)"
612 SysLog "usb_modeswitch: use overriding config file $config; make sure this is intended"
613 SysLog "usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files"
615 set rc [open $config r]
616 set configContent [read $rc]
617 close $rc
619 return $configContent
624 # end of proc {ConfigGet}
626 proc {Log} {msg} {
628 global flags device loginit
630 if {$flags(logging) == 0} {return}
632 if $flags(logwrite) {
633 if [string length $loginit] {
634 exec echo "\nUSB_ModeSwitch log from [clock format [clock seconds]]" >/var/log/usb_modeswitch_$device
635 exec echo "$loginit" >>/var/log/usb_modeswitch_$device
636 set loginit ""
638 exec echo $msg >>/var/log/usb_modeswitch_$device
639 } else {
640 append loginit "\n$msg"
644 # end of proc {Log}
647 # Writing the log file and exit
648 proc {SafeExit} {} {
650 global flags
651 set flags(logwrite) 1
652 Log ""
653 exit
656 # end of proc {SafeExit}
659 proc {SymLinkName} {path} {
660 global device flags
662 proc {hasInterrupt} {ifDir} {
663 if {[llength [glob -nocomplain $ifDir/ttyUSB*]] == 0} {
664 Log " no ttyUSB interface - skip endpoint check"
665 return 0
667 foreach epDir [glob -nocomplain $ifDir/ep_*] {
668 set e [file tail $epDir]
669 Log " check $e ..."
670 if [file exists $epDir/type] {
671 set rc [open $epDir/type r]
672 set type [read $rc]
673 close $rc
674 if [regexp {Interrupt} $type] {
675 Log " $e has interrupt transfer type"
676 return 1
680 return 0
683 set loginit "usb_modeswitch called with --symlink-name\n parameter: $path\n"
685 # In case the device path is returned as /class/tty/ttyUSB,
686 # get the USB device path from linked tree "device"
687 set linkpath /sys$path/device
688 if [file exists $linkpath] {
689 if {[file type $linkpath] == "link"} {
690 set rawpath [file readlink $linkpath]
691 set trimpath [regsub -all {\.\./} $rawpath {}]
692 if [file isdirectory /sys/$trimpath] {
693 append loginit "\n Use path $path\n"
694 set path /$trimpath
698 if {![regexp {([0-9]+-[0-9]+[\.0-9]*:[^/]*).*(ttyUSB[0-9]+)} $path d myDev myPort]} {
699 if $flags(logging) {
700 set device [clock clicks]
701 set flags(logwrite) 1
702 Log "$loginit\nThis is not a ttyUSB port. Abort"
704 return ""
707 set device ttyUSB_$myDev
708 set flags(logwrite) 1
709 Log "$loginit\nMy name is $myPort\n"
711 if {![regexp {(.*?[0-9]+)\.([0-9]+)/ttyUSB} /sys$path d ifRoot ifNum]} {
712 Log "Could not find interface in path\n $path. Abort"
713 return ""
716 set ifDir $ifRoot.$ifNum
718 Log "Check my endpoints ...\n in $ifDir"
719 if [hasInterrupt $ifDir] {
720 Log "\n--> I am an interrupt port"
721 set rightPort 1
722 } else {
723 Log "\n--> I am not an interrupt port\n"
724 set rightPort 0
727 # There are devices with more than one interrupt interface.
728 # Assume that the lowest of these is usable. Check all
729 # possible lower interfaces
731 if { $rightPort && ($ifNum > 0) } {
732 Log "\nLook for lower ports with interrupt endpoints"
733 for {set i 0} {$i < $ifNum} {incr i} {
734 set ifDir $ifRoot.$i
735 Log " in ifDir $ifDir ..."
736 if [hasInterrupt $ifDir] {
737 Log "\n--> found an interrupt interface below me\n"
738 set rightPort 0
739 break
743 if {$rightPort == 0} {
744 Log "Return empty name and exit"
745 return ""
748 Log "\n--> No interrupt interface below me\n"
750 cd /dev
751 set idx 2
752 set symlinkName "gsmmodem"
753 while {$idx < 256} {
754 if {![file exists $symlinkName]} {
755 set placeholder [open /dev/$symlinkName w]
756 close $placeholder
757 break
759 set symlinkName gsmmodem$idx
760 incr idx
762 if {$idx == 256} {return ""}
764 Log "Return symlink name \"$symlinkName\" and exit"
765 return $symlinkName
768 # end of proc {SymLinkName}
771 # Add USB ID to list of devices needing later treatment
772 proc {AddToList} {name id} {
774 set listfile /var/lib/usb_modeswitch/$name
775 if [file exists $listfile] {
776 set rc [open $listfile r]
777 set buffer [read $rc]
778 close $rc
779 if [string match *$id* $buffer] {
780 return
782 set idList [split [string trim $buffer] \n]
784 lappend idList $id
785 set buffer [join $idList "\n"]
786 if [catch {set lc [open $listfile w]}] {return}
787 puts $lc $buffer
788 close $lc
791 # end of proc {AddToList}
794 proc {CheckSuccess} {devdir} {
796 global config usb flags
798 # For Cisco AM10, target device not on same port
799 if {$usb(idVendor) == "1307" && $usb(idProduct) == "1169"} {
800 set devdir [string range $devdir 0 end-1]2
802 set ifdir [file tail [IfDir 0 $devdir]]
803 if {[string length $config(TargetClass)] || [string length $config(Configuration)]} {
804 set config(TargetVendor) $usb(idVendor)
805 set config(TargetProduct) $usb(idProduct)
807 Log "Check success of mode switch for max. $config(CheckSuccess) seconds ..."
809 set expected 1
810 for {set i 1} {$i <= $config(CheckSuccess)} {incr i} {
811 after 1000
812 if {![file isdirectory $devdir]} {
813 Log " Wait for device file system ($i sec.) ..."
814 continue
815 } else {
816 Log " Read attributes ..."
818 set ifdir [IfDir 0 $devdir]
819 if {$ifdir == ""} {continue}
820 set ifdir [file tail $ifdir]
821 if {![ReadUSBAttrs $devdir $ifdir]} {
822 Log " Essential attributes are missing, continue wait ..."
823 continue
825 if [string length $config(Configuration)] {
826 if {$usb(bConfigurationValue) != $config(Configuration)} {continue}
828 if [string length $config(TargetClass)] {
829 if {![regexp $usb($ifdir/bInterfaceClass) $config(TargetClass)]} {
830 if {$config(class) != $usb($ifdir/bInterfaceClass} {
831 set expected 0
832 } else {continue}
835 if {![regexp $usb(idVendor) $config(TargetVendor)]} {
836 if {![regexp $usb(idVendor) $config(vendor)]} {
837 set expected 0
838 } else {continue}
840 if {![regexp $usb(idProduct) $config(TargetProduct)]} {
841 if {![regexp $usb(idProduct) $config(product)]} {
842 set expected 0
843 } else {continue}
845 # Arriving here means that device attributes have changed
846 if $expected {
847 Log " All attributes matched"
848 } else {
849 if [regexp -nocase {/[0-9a-f]+:#} $flags(config)] {
850 Log " idProduct has changed after generic mode-switch, assume success"
851 } else {
852 Log " Attributes are different but target values are unexpected:"
853 LogAttributes
856 break
858 if {$i > 20} {return 0} else {return 1}
861 # end of proc {CheckSuccess}
864 proc {IfDir} {iface devdir} {
866 set allfiles [glob -nocomplain $devdir/*]
867 set files [glob -nocomplain $devdir/*.$iface]
868 if {[llength $files] == 0} {
869 return ""
871 set ifdir [lindex $files 0]
872 if {![file isdirectory $ifdir]} {
873 return ""
875 return $ifdir
878 # end of proc {IfDir}
880 proc {IfClass} {iface devdir} {
882 set ifdir [IfDir $iface $devdir]
884 if {![file exists $ifdir/bInterfaceClass]} {
885 return -1
887 set rc [open $ifdir/bInterfaceClass r]
888 set c [read $rc]
889 close $rc
890 return [string trim $c]
893 # end of proc {IfClass}
896 proc {SysLog} {msg} {
898 global flags
899 if {![info exists flags(logger)]} {
900 set flags(logger) ""
901 foreach fn {/bin/logger /usr/bin/logger} {
902 if [file exists $fn] {
903 set flags(logger) $fn
906 Log "Logger is $flags(logger)"
908 if {$flags(logger) == ""} {
909 Log "Can't add system message, no syslog helper found"
910 return
912 catch {exec $flags(logger) -p syslog.notice "$msg" 2>/dev/null}
915 # end of proc {SysLog}
917 proc {SetStorageDelay} {secs} {
919 Log "Adjust delay for USB storage devices ..."
920 set attrib /sys/module/usb_storage/parameters/delay_use
921 if {![file exists $attrib]} {
922 Log "Error: could not find delay_use attribute"
923 return
925 if [catch {set ch [open $attrib r+]} err] {
926 Log "Error: could not access delay_use attribute: $err"
927 return
929 if {[read $ch] < $secs} {
930 seek $ch 0 start
931 puts -nonewline $ch $secs
932 Log " Delay set to $secs seconds\n"
933 } else {
934 Log " Current value is higher than $secs. Leave it alone\n"
936 close $ch
939 # end of proc {SetStorageDelay}
941 proc {CheckMBIM} {} {
943 set kversion [exec uname -r]
944 if [llength [glob -nocomplain /lib/modules/$kversion/kernel/drivers/net/usb/cdc_mbim*]] {return 1}
945 if [file exists /sys/bus/usb/drivers/cdc_mbim] {return 1}
946 return 0
950 proc {CheckQMI} {} {
952 set kversion [exec uname -r]
953 if [llength [glob -nocomplain /lib/modules/$kversion/kernel/drivers/net/usb/qmi_wwan*]] {return 1}
954 if [file exists /sys/bus/usb/drivers/cdc_mbim] {return 1}
955 return 0
959 proc {PantechAutoSwitch} {} {
961 global config flags
962 if {$config(PantechMode) == 3} {return 1}
963 if {$config(PantechMode) == 1} {
964 if {"$config(vendor):$config(product)" == "10a9:6080"} {
965 set flags(config) [regsub {PantechMode *= *1} $flags(config) "PantechMode=2"]
966 Log " PantechMode changed to 2"
967 return 0
968 } elseif [CheckQMI] {
969 set flags(config) [regsub {PantechMode *= *1} $flags(config) "PantechMode=4"]
970 Log " PantechMode changed to 4"
971 return 0
972 } else {
973 return 1
975 } else {return 0}
979 proc UnbindDriver {devdir ifdir} {
981 set att $devdir/$ifdir/driver/unbind
982 if [file exists $att] {
983 Log "Unbinding driver"
984 exec echo -n "$ifdir" > $att
989 proc {LogAttributes} {} {
991 global flags usb
992 if $flags(logging) {
993 set attrList {idVendor idProduct bConfigurationValue manufacturer product serial}
994 foreach attr [lsort [array names usb]] {
995 Log " [format %-26s $attr:] $usb($attr)"
1001 proc {HasFF} {devdir} {
1003 set i 0
1004 while {[set dir [IfDir $i $devdir]] != ""} {
1005 set c [exec cat $dir/bInterfaceClass]
1006 if {$c == "ff"} {return 1}
1007 incr i
1009 return 0
1014 # The actual entry point
1015 Main $argv $argc