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.2.1 package
13 # (C) Josua Dietze 2009-2015
15 set arg0
[lindex $argv 0]
16 if [regexp {\.tcl
$} $arg0] {
17 if [file exists
$arg0] {
18 set argv
[lrange $argv 1 end
]
24 # Setting of these switches is done in the global config
25 # file (/etc/usb_modeswitch.conf) if available
28 set flags
(noswitching
) 0
29 set flags
(stordelay
) 0
31 # Execution starts at file bottom
33 proc {Main
} {argv argc
} {
35 global scsi usb config match device flags setup devdir loginit
39 Log
"[ParseGlobalConfig]"
41 # The facility to add a symbolic link pointing to the
42 # ttyUSB port which provides interrupt transfer, i.e.
43 # the port to connect through.
44 # Will check for interrupt endpoint in ttyUSB port (lowest if
45 # there is more than one); if found, return "gsmmodem[n]" name
46 # to udev for symlink creation
48 # This is run once for every port of LISTED devices by
51 if {[lindex $argv 0] == "--symlink-name"} {
52 puts -nonewline [SymLinkName
[lindex $argv 1]]
56 if {[lindex $argv 0] == "--switch-systemd"} {
57 set device
[string trim
[lindex $argv 1] "/-"]
58 set device
[regsub {/} $device "-"]
59 set argList
[list "" $device]
60 Log
"\nStarted via systemd"
62 if {[lindex $argv 0] == "--switch-upstart"} {
63 Log
"\nStarted via upstart"
65 set argList
[split [lindex $argv 1] /]
66 if [string length
[lindex $argList 1]] {
67 set device
[lindex $argList 1]
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"
83 if {![regexp -- {--switch-} [lindex $argv 0]]} {
84 Log
"\nNo command given. Exit"
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"
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
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"
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 # new udev rules file, got to check class of first interface
118 Log
"Could not determine device dir from udev values! Exit"
124 set dev_top
[lindex $argList 0]
125 regexp {(.
*?
):} $dev_top d dev_top
128 set devdir
/sys
/bus
/usb
/devices
/$dev_top
129 if {![file isdirectory
$devdir]} {
130 Log
"Top device directory not found ($devdir)! Exit"
133 Log
"Use top device dir $devdir"
137 Log
"Check class of first interface ..."
138 set config
(class
) [IfClass
0]
140 Log
" No access to interface 0. Exit"
143 Log
" Interface class is $config(class)."
144 if {$config(class
) == "08" ||
$config(class
) == "03"} {
146 Log
"No install mode found. Aborting"
150 set ifdir
[file tail
[IfDir
$iface]]
151 regexp {:([0-9]+\.
[0-9]+)$} $ifdir d iface
153 set flags
(logwrite
) 1
155 # Mapping of the short string identifiers (in the config
156 # file names) to the long name used here
158 # If we need them it's a snap to add new attributes here!
160 set match
(sVe
) scsi
(vendor
)
161 set match
(sMo
) scsi
(model
)
162 set match
(sRe
) scsi
(rev
)
163 set match
(uMa
) usb
(manufacturer
)
164 set match
(uPr
) usb
(product
)
165 set match
(uSe
) usb
(serial
)
168 # Now reading the USB attributes
169 if {![ReadUSBAttrs
$devdir]} {
170 Log
"USB attributes not found in sysfs tree. Exit"
173 set config
(vendor
) $usb(idVendor
)
174 set config
(product
) $usb(idProduct
)
178 Log
"\n----------------\nUSB values from sysfs:"
179 foreach attr
{manufacturer product serial
} {
180 Log
" $attr\t$usb($attr)"
182 Log
"----------------"
185 if $flags(noswitching
) {
186 SysLog
"usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)"
187 Log
"\nSwitching globally disabled. Exit"
191 if {$usb(bNumConfigurations
) == "1"} {
192 set configParam
"-u -1"
193 Log
"bNumConfigurations is 1 - don't check for active configuration"
198 # Check (and switch) for operating system if Huawei device present
200 set flags
(os
) "linux"
201 if {$usb(idVendor
) == "12d1" && [regexp -nocase {android
} [exec cat
/proc/version
]]} {
202 set flags
(os
) "android"
204 if {$flags(os
) == "android"} {
205 set configList
[ConfigGet conflist
$usb(idVendor
):#android]
207 set configList
[ConfigGet conflist
$usb(idVendor
):$usb(idProduct
)]
210 if {[llength $configList] == 0} {
211 Log
"Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exit"
214 Log
"ConfigList: $configList"
216 # Check if there is more than one config file for this USB ID,
217 # which would make an attribute test necessary. If so, check if
218 # SCSI values are needed
221 if {[llength $configList] > 1} {
222 if [regexp {:s
} $configList] {
227 if [ReadSCSIAttrs
$devdir:$iface] {
228 Log
"----------------\nSCSI values from sysfs:"
229 foreach attr
{vendor model rev
} {
230 Log
" $attr\t$scsi($attr)"
232 Log
"----------------"
234 Log
"Could not get SCSI attributes, exclude devices with SCSI match"
237 Log
"SCSI attributes not needed, move on"
240 # General wait - some devices need this
243 # Now check for a matching config file. Matching is done
247 foreach mconfig
$configList {
249 # skipping installer leftovers like "*.rpmnew"
250 if [regexp {\.
(dpkg|rpm
)} $mconfig] {continue}
252 Log
"Check config: $mconfig"
253 if [MatchDevice
$mconfig] {
254 Log
"! matched. Read config data"
255 set flags
(config
) $mconfig
256 if [string length
$usb(busnum
)] {
257 set busParam
"-b [string trimleft $usb(busnum) 0]"
258 set devParam
"-g [string trimleft $usb(devnum) 0]"
263 set configBuffer
[ConfigGet conffile
$mconfig]
264 ParseDeviceConfig
$configBuffer
265 if [regexp -nocase {/[0-9a-f
]+:#} $flags(config)] {
266 Log
"Note: Using generic manufacturer configuration for \"$flags(os)\""
268 if {$config(waitBefore
) != ""} {
269 Log
"Delay time of $config(waitBefore) seconds"
270 append config
(waitBefore
) "000"
271 after $config(waitBefore
)
272 Log
" wait is over, start mode switch"
274 if {$config(noMBIMCheck
)==0 && $usb(bNumConfigurations
) > 1} {
275 Log
"Device may have an MBIM configuration, check driver ..."
277 Log
" driver for MBIM devices is available"
278 Log
"Find MBIM configuration number ..."
279 if [catch {set cfgno
[exec /usr
/sbin
/usb_modeswitch
-j -Q $busParam $devParam -v $usb(idVendor
) -p $usb(idProduct
)]} err
] {
280 Log
"Error when trying to find MBIM configuration, switch to legacy modem mode"
282 set cfgno
[string trim
$cfgno]
284 set config
(Configuration
) $cfgno
285 set config
(driverModule
) ""
286 set configBuffer
"Configuration=$cfgno"
288 Log
" No MBIM configuration found, switch to legacy modem mode"
292 Log
" no MBIM driver found, switch to legacy modem mode"
296 # Now we are actually switching
298 Log
"Command to be run:\nusb_modeswitch -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f \$configBuffer"
299 set report
[exec /usr
/sbin
/usb_modeswitch
-W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor
) -p $usb(idProduct
) -f "$configBuffer" 2>@1]
300 Log
"\nVerbose debug output of usb_modeswitch and libusb follows"
301 Log
"(Note that some USB errors are to be expected in the process)"
302 Log
"--------------------------------"
304 Log
"--------------------------------"
305 Log
"(end of usb_modeswitch output)\n"
307 set report
[exec /usr
/sbin
/usb_modeswitch
-Q -D -s 20 $configParam $busParam $devParam -v $usb(idVendor
) -p $usb(idProduct
) -f "$configBuffer" 2>@1]
311 Log
"* no match, don't use this config"
315 # Switching is complete; success checking was either
316 # done by usb_modeswitch and logged via syslog OR bus/dev
317 # parameter were used; then we do check for success HERE
319 if [regexp {ok
:busdev
} $report] {
320 if [CheckSuccess
$devdir] {
321 Log
"Mode switching was successful, found $usb(idVendor):$usb(idProduct) ($usb(manufacturer): $usb(product))"
322 SysLog
"usb_modeswitch: switched to $usb(idVendor):$usb(idProduct) on [format %03d $usb(busnum)]/[format %03d $usb(devnum)]"
324 Log
"\nTarget config not matching - current values are"
326 Log
"\nMode switching may have failed. Exit"
330 if {![file isdirectory
$devdir]} {
331 Log
"Device directory in sysfs is gone! Something went wrong, abort"
334 if {![regexp {ok
:} $report]} {
335 Log
"\nCore program reported switching failure. Exit"
338 # Give the device another second if it's not fully back yet
339 if {![file exists
$devdir/idProduct
]} {
342 ReadUSBAttrs
$devdir $ifdir
345 # Now checking for bound drivers (only for class 0xff)
347 if {$config(driverModule
) != "" && $usb($ifdir/bInterfaceClass
) != "" && [regexp {ok
:} $report]} {
348 if {$usb($ifdir/bInterfaceClass
) != "ff"} {
349 set config
(driverModule
) ""
350 Log
" No vendor-specific class found, skip driver check"
354 # If module is set (it is by default), driver shall be loaded.
355 # If not, then NoDriverLoading is active
357 if {$config(driverModule
) != ""} {
358 if {[string length
"$usb(idVendor)$usb(idProduct)"] < 8} {
359 if {![regexp {ok
:(\w
{4}):(\w
{4})} $report d usb
(idVendor
) usb
(idProduct
)]} {
360 Log
"No target vendor/product ID found or given, can't continue. Abort"
364 # wait for any drivers to bind automatically
366 Log
"Now check for bound driver ..."
367 if {![file exists
$devdir/$ifdir/driver
]} {
368 Log
" no driver has bound to interface 0 yet"
369 AddToList link_list
$usb(idVendor
):$usb(idProduct
)
371 # If device is known, the sh wrapper will take care, else:
372 if {[InBindList
$usb(idVendor
):$usb(idProduct
)] == 0} {
373 Log
"Device is not in \"bind_list\" yet, bind it now"
376 CheckDriverBind
$usb(idVendor
) $usb(idProduct
)
378 # Old/slow systems may take a while to create the devices
380 while {![file exists
$devdir/$ifdir/driver
]} {
381 if {$counter == 14} {break}
385 if {$counter == 14} {
386 Log
" driver binding failed"
388 Log
" driver was bound to the device"
389 AddToList bind_list
$usb(idVendor
):$usb(idProduct
)
393 Log
" driver has bound, device is known"
394 if {[llength [glob -nocomplain $devdir/$ifdir/ttyUSB
*]] > 0} {
395 AddToList link_list
$usb(idVendor
):$usb(idProduct
)
399 # Just in case "NoDriverLoading" was added after the first bind
400 RemoveFromBindList
$usb(idVendor
):$usb(idProduct
)
403 if [regexp {ok
:$} $report] {
404 # "NoDriverLoading" was set
405 Log
"No driver check or bind for this device"
408 # In newer kernels there is a switch to avoid the use of a device
409 # reset (e.g. from usb-storage) which would possibly switch back
410 # a mode-switching device to initial mode
411 if [regexp {ok
:} $report] {
412 Log
"Check for AVOID_RESET_QUIRK kernel attribute"
413 if [file exists
$devdir/avoid_reset_quirk
] {
414 if [catch {exec echo
"1" >$devdir/avoid_reset_quirk
2>/dev
/null
} err
] {
415 Log
" Error setting the attribute: $err"
417 Log
" AVOID_RESET_QUIRK activated"
420 Log
" not present in this kernel"
424 Log
"\nAll done, exit\n"
431 proc {ReadSCSIAttrs
} {topdir
} {
436 Log
"Check storage tree in sysfs ..."
437 while {$counter < 20} {
438 Log
" loop $counter/20"
439 if {![file isdirectory
$sysdir]} {
440 # Device is gone. Unplugged? Switched by kernel?
441 Log
" sysfs device tree is gone; abort SCSI value check"
444 # Searching the storage/SCSI tree; might take a while
445 if {[set dirList
[glob -nocomplain $topdir/host
*]] != ""} {
446 set sysdir
[lindex $dirList 0]
447 if {[set dirList
[glob -nocomplain $sysdir/target
*]] != ""} {
448 set sysdir
[lindex $dirList 0]
449 regexp {.
*target
(.
*)} $sysdir d subdir
450 if {[set dirList
[glob -nocomplain $sysdir/$subdir*]] != ""} {
451 set sysdir
[lindex $dirList 0]
452 if [file exists
$sysdir/vendor
] {
453 Log
" Storage tree is ready"
462 if {$counter == 20} {
463 Log
"SCSI tree not found; you may want to check if this path/file exists:"
464 Log
"$sysdir/vendor\n"
468 Log
"Read SCSI values ..."
469 foreach attr
{vendor model rev
} {
470 if [file exists
$sysdir/$attr] {
471 set rc
[open $sysdir/$attr r
]
472 set scsi
($attr) [read -nonewline $rc]
476 Log
"Warning: SCSI attribute \"$attr\" not found."
482 # end of proc {ReadSCSIAttrs}
485 proc {ReadUSBAttrs
} {dir args
} {
489 set attrList
{idVendor idProduct bConfigurationValue manufacturer product serial devnum busnum bNumConfigurations
}
490 set mandatoryList
{idVendor idProduct bNumConfigurations
}
493 lappend attrList
"$args/bInterfaceClass"
494 lappend mandatoryList
"$args/bInterfaceClass"
496 foreach attr
$attrList {
497 if [file exists
$dir/$attr] {
498 set rc
[open $dir/$attr r
]
499 set usb
($attr) [string trim
[read -nonewline $rc]]
503 if {[lsearch $mandatoryList $attr] > -1} {
506 if {$attr == "serial"} {continue}
507 Log
" Warning: USB attribute \"$attr\" not found"
513 # end of proc {ReadUSBAttrs}
516 proc {MatchDevice
} {config
} {
518 global scsi usb match
520 set devinfo
[file tail
$config]
521 set infoList
[split $devinfo :]
522 set stringList
[lrange $infoList 2 end
]
523 if {[llength $stringList] == 0} {return 1}
525 foreach teststring
$stringList {
526 if {$teststring == "?"} {return 0}
527 set tokenList
[split $teststring =]
528 set id
[lindex $tokenList 0]
529 set matchstring
[lindex $tokenList 1]
531 regsub -all {_
} $matchstring { } blankstring
532 Log
"match $match($id)"
533 Log
" string1 (exact): $matchstring"
534 Log
" string2 (blanks): $blankstring"
535 Log
" device string: [set $match($id)]"
536 if {!([string match
*$matchstring* [set $match($id)]] ||
[string match
*$blankstring* [set $match($id)]])} {
543 # end of proc {MatchDevice}
546 proc {ParseGlobalConfig
} {} {
550 set places
[list /etc
/usb_modeswitch.conf
/etc
/sysconfig
/usb_modeswitch
/etc
/default/usb_modeswitch
]
551 foreach cfg
$places {
552 if [file exists
$cfg] {
557 if {$configFile == ""} {return}
559 set rc
[open $configFile r
]
562 if [regexp {^
#} [string trim $line]] {continue}
563 if [regexp {DisableSwitching
\s
*=\s
*([^
\s
]+)} $line d val
] {
564 if [regexp -nocase {1|yes|true
} $val] {
565 set flags
(noswitching
) 1
568 if [regexp {EnableLogging
\s
*=\s
*([^
\s
]+)} $line d val
] {
569 if [regexp -nocase {1|yes|true
} $val] {
573 if [regexp {SetStorageDelay
\s
*=\s
*([^
\s
]+)} $line d val
] {
574 if [regexp {\d
+} $val] {
575 set flags
(stordelay
) $val
580 return "Use global config file: $configFile"
583 # end of proc {ParseGlobalConfig}
586 proc ParseDeviceConfig
{configContent
} {
589 set config
(driverModule
) ""
590 set config
(driverIDPath
) ""
591 set config
(waitBefore
) ""
592 set config
(targetVendor
) ""
593 set config
(targetProduct
) ""
594 set config
(targetClass
) ""
595 set config
(Configuration
) ""
596 set config
(noMBIMCheck
) 0
597 set config
(checkSuccess
) 20
600 if [regexp -line {^
[^
#]*?TargetVendor.*?=.*?0x(\w+).*?$} $configContent d config(targetVendor)] {
601 Log
"config: TargetVendor set to $config(targetVendor)"
603 if [regexp -line {^
[^
#]*?TargetProduct.*?=.*?0x(\w+).*?$} $configContent d config(targetProduct)] {
604 Log
"config: TargetProduct set to $config(targetProduct)"
606 if [regexp -line {^
[^
#]*?TargetProductList.*?=.*?"([0-9a-fA-F,]+).*?$} $configContent d config(targetProduct)] {
607 Log
"config: TargetProductList set to $config(targetProduct)"
609 if [regexp -line {^
[^
#]*?TargetClass.*?=.*?0x(\w+).*?$} $configContent d config(targetClass)] {
610 Log
"config: TargetClass set to $config(targetClass)"
612 if [regexp -line {^
[^
#]*?Configuration.*?=.*?([0-9]+).*?$} $configContent d config(Configuration)] {
613 Log
"config: Configuration (target) set to $config(Configuration)"
615 if [regexp -line {^
[^
#]*?DriverModule.*?=.*?(\w+).*?$} $configContent d config(driverModule)] {
616 Log
"config: DriverModule set to $config(driverModule)"
618 if [regexp -line {^
[^
#]*?DriverIDPath.*?=.*?"?([/\-\w]+).*?$} $configContent d config(driverIDPath)] {
619 Log
"config: DriverIDPath set to $config(driverIDPath)"
621 if [regexp -line {^
[^
#]*?CheckSuccess.*?=.*?([0-9]+).*?$} $configContent d config(checkSuccess)] {
622 Log
"config: CheckSuccess set to $config(checkSuccess)"
624 if [regexp -line {^
[^
#]*?WaitBefore.*?=.*?([0-9]+).*?$} $configContent d config(waitBefore)] {
625 Log
"config: WaitBefore set to $config(waitBefore)"
627 if [regexp -line {^
[^
#]*?NoMBIMCheck.*?=.*?([0-9]+).*?$} $configContent d config(noMBIMCheck)] {
628 Log
"config: noMBIMCheck set to $config(noMBIMCheck)"
630 if [regexp -line {^
[^
#]*?NoDriverLoading.*?=.*?(1|yes|true).*?$} $configContent] {
632 Log
"config: NoDriverLoading is set to active"
635 # For general driver loading; TODO: add respective device names.
636 # Presently only useful for HSO devices (which are recounted now)
638 if {$config(driverModule
) == ""} {
639 set config
(driverModule
) "option"
640 set config
(driverIDPath
) "/sys/bus/usb-serial/drivers/option1"
642 if {$config(driverIDPath
) == ""} {
643 set config
(driverIDPath
) "/sys/bus/usb/drivers/$config(driverModule)"
646 Log
"Driver module is \"$config(driverModule)\", ID path is $config(driverIDPath)\n"
648 Log
"Driver will not be handled by usb_modeswitch"
650 set config
(waitBefore
) [string trimleft
$config(waitBefore
) 0]
653 # end of proc {ParseDeviceConfig}
656 proc ConfigGet
{command config
} {
658 global setup usb flags
663 # Unpackaged configs first; sorting is essential for priority
664 set configList
[lsort -decreasing [glob -nocomplain $setup(dbdir_etc
)/$config*]]
665 set configList
[concat $configList [lsort -decreasing [glob -nocomplain $setup(dbdir
)/$config*]]]
666 eval lappend configList
[glob -nocomplain $setup(dbdir
)/$usb(idVendor
):#$flags(os)]
667 if [file exists
$setup(dbdir
)/configPack.tar.gz
] {
668 Log
"Found packed config collection $setup(dbdir)/configPack.tar.gz"
669 if [catch {set packedList
[exec tar
-tzf $setup(dbdir
)/configPack.tar.gz
2>/dev
/null
]} err
] {
670 Log
"Error: problem opening config package; tar returned\n $err"
673 set packedList
[split $packedList \n]
674 set packedConfigList
[lsort -decreasing [lsearch -glob -all -inline $packedList $config*]]
675 lappend packedConfigList
[lsearch -inline $packedList $usb(idVendor
):#$flags(os)]
676 # Now add packaged configs with a mark, again sorted for priority
677 foreach packedConfig
$packedConfigList {
678 lappend configList
"pack/$packedConfig"
684 if [regexp {^
pack/} $config] {
685 set config
[regsub {pack/} $config {}]
686 Log
"Extract config $config from collection $setup(dbdir)/configPack.tar.gz"
687 set configContent
[exec tar
-xzOf $setup(dbdir
)/configPack.tar.gz
$config 2>/dev
/null
]
689 if [regexp [list $setup(dbdir_etc
)] $config] {
690 Log
"Use config file from override folder $setup(dbdir_etc)"
691 SysLog
"usb_modeswitch: use overriding config file $config; make sure this is intended"
692 SysLog
"usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files"
694 set rc
[open $config r
]
695 set configContent
[read $rc]
698 return $configContent
703 # end of proc {ConfigGet}
707 global flags device loginit
709 if {$flags(logging
) == 0} {return}
711 if $flags(logwrite
) {
712 if [string length
$loginit] {
713 exec echo
"\nUSB_ModeSwitch log from [clock format [clock seconds]]" >/var
/log
/usb_modeswitch_
$device
714 exec echo
"$loginit" >>/var
/log
/usb_modeswitch_
$device
717 exec echo
$msg >>/var
/log
/usb_modeswitch_
$device
719 append loginit
"\n$msg"
726 # Writing the log file and exit
730 set $flags(logwrite
) 1
735 # end of proc {SafeExit}
738 proc {SymLinkName
} {path
} {
741 proc {hasInterrupt
} {ifDir
} {
742 if {[llength [glob -nocomplain $ifDir/ttyUSB
*]] == 0} {
743 Log
" no ttyUSB interface - skip endpoint check"
746 foreach epDir
[glob -nocomplain $ifDir/ep_
*] {
747 set e
[file tail
$epDir]
749 if [file exists
$epDir/type
] {
750 set rc
[open $epDir/type r
]
753 if [regexp {Interrupt
} $type] {
754 Log
" $e has interrupt transfer type"
762 set loginit
"usb_modeswitch called with --symlink-name\n parameter: $path\n"
764 # In case the device path is returned as /class/tty/ttyUSB,
765 # get the USB device path from linked tree "device"
766 set linkpath
/sys
$path/device
767 if [file exists
$linkpath] {
768 if {[file type
$linkpath] == "link"} {
769 set rawpath
[file readlink
$linkpath]
770 set trimpath
[regsub -all {\.
\.
/} $rawpath {}]
771 if [file isdirectory
/sys
/$trimpath] {
772 append loginit
"\n Use path $path\n"
778 if {![regexp {ttyUSB
[0-9]+} $path myPort
]} {
780 set device
[clock clicks
]
781 Log
"$loginit\nThis is not a ttyUSB port. Abort"
787 Log
"$loginit\nMy name is $myPort\n"
789 if {![regexp {(.
*?
[0-9]+)\.
([0-9]+)/ttyUSB
} /sys
$path d ifRoot ifNum
]} {
790 Log
"Could not find interface in path\n $path. Abort"
794 set ifDir
$ifRoot.
$ifNum
796 Log
"Check my endpoints ...\n in $ifDir"
797 if [hasInterrupt
$ifDir] {
798 Log
"\n--> I am an interrupt port"
801 Log
"\n--> I am not an interrupt port\n"
805 # There are devices with more than one interrupt interface.
806 # Assume that the lowest of these is usable. Check all
807 # possible lower interfaces
809 if { $rightPort && ($ifNum > 0) } {
810 Log
"\nLook for lower ports with interrupt endpoints"
811 for {set i
0} {$i < $ifNum} {incr i
} {
813 Log
" in ifDir $ifDir ..."
814 if [hasInterrupt
$ifDir] {
815 Log
"\n--> found an interrupt interface below me\n"
821 if {$rightPort == 0} {
822 Log
"Return empty name and exit"
826 Log
"\n--> No interrupt interface below me\n"
830 set symlinkName
"gsmmodem"
832 if {![file exists
$symlinkName]} {
833 set placeholder
[open /dev
/$symlinkName w
]
837 set symlinkName gsmmodem
$idx
840 if {$idx == 256} {return ""}
842 Log
"Return symlink name \"$symlinkName\" and exit"
846 # end of proc {SymLinkName}
849 # Load and bind driver (default "option")
851 proc {CheckDriverBind
} {vid
pid} {
854 foreach fn
{/sbin
/modprobe
/usr
/sbin
/modprobe
} {
855 if [file exists
$fn] {
859 Log
"Module loader is $loader"
861 set idfile
$config(driverIDPath
)/new_id
862 if {![file exists
$idfile]} {
864 Log
"Can't do anymore without module loader; get \"modtools\"!"
867 Log
"\nTry to load module \"$config(driverModule)\""
868 if [catch {set result
[exec $loader -v $config(driverModule
)]} err
] {
869 Log
" Running \"$loader $config(driverModule)\" gave an error:\n $err"
871 Log
" Module was loaded successfully:\n$result"
874 Log
"Module is active already"
878 if [file exists
$idfile] {
885 Log
"Try to add ID to driver \"$config(driverModule)\""
886 SysLog
"usb_modeswitch: add device ID $vid:$pid to driver \"$config(driverModule)\""
887 SysLog
"usb_modeswitch: please report the device ID to the Linux USB developers!"
888 if [catch {exec echo
"$vid $pid ff" >$idfile} err
] {
889 Log
" Error adding ID to driver:\n $err"
891 Log
" ID added to driver; check for new devices in /dev"
894 Log
" \"$idfile\" not found, check if kernel version is at least 2.6.27"
895 Log
"Fall back to \"usbserial\""
896 set config
(driverModule
) usbserial
897 Log
"\nTry to unload driver \"usbserial\""
898 if [catch {exec $loader -r usbserial
} err
] {
899 Log
" Running \"$loader -r usbserial\" gave an error:\n $err"
900 Log
"No more fallbacks"
904 Log
"\nTry to load driver \"usbserial\" with device IDs"
905 if [catch {set result
[exec $loader -v usbserial vendor
=0x
$vid product
=0x
$pid]} err
] {
906 Log
" Running \"$loader usbserial\" gave an error:\n $err"
908 Log
" Driver was loaded successfully:\n$result"
913 # end of proc {CheckDriverBind}
916 # Check if USB ID is listed as needing driver binding
917 proc {InBindList
} {id
} {
919 set listfile
/var
/lib
/usb_modeswitch
/bind_list
920 if {![file exists
$listfile]} {return 0}
921 set rc
[open $listfile r
]
922 set buffer
[read $rc]
924 if [string match
*$id* $buffer] {
925 Log
"Found $id in bind_list"
928 Log
"No $id in bind_list"
933 # end of proc {InBindList}
935 # Add USB ID to list of devices needing later treatment
936 proc {AddToList
} {name id
} {
938 set listfile
/var
/lib
/usb_modeswitch
/$name
939 set oldlistfile
/etc
/usb_modeswitch.d
/bind_list
941 if {($name == "bind_list") && [file exists
$oldlistfile] && ![file exists
$listfile]} {
942 if [catch {file rename $oldlistfile $listfile} err
] {
943 Log
"Error renaming the old bind list file ($err)"
948 if [file exists
$listfile] {
949 set rc
[open $listfile r
]
950 set buffer
[read $rc]
952 if [string match
*$id* $buffer] {
955 set idList
[split [string trim
$buffer] \n]
958 set buffer
[join $idList "\n"]
959 if [catch {set lc
[open $listfile w
]}] {return}
964 # end of proc {AddToList}
967 # Remove USB ID from bind list (NoDriverLoading is set)
968 proc {RemoveFromBindList
} {id
} {
970 set listfile
/var
/lib
/usb_modeswitch
/bind_list
971 if [file exists
$listfile] {
972 set rc
[open $listfile r
]
973 set buffer
[read $rc]
975 set idList
[split [string trim
$buffer] \n]
979 set idx
[lsearch $idList $id]
981 set idList
[lreplace $idList $idx $idx]
985 if {[llength $idList] == 0} {
986 file delete
$listfile
989 set buffer
[join $idList "\n"]
990 if [catch {set lc
[open $listfile w
]}] {return}
995 # end of proc {RemoveFromBindList}
998 proc {CheckSuccess
} {devdir
} {
1000 global config usb flags
1001 set ifdir
[file tail
[IfDir
0]]
1003 if {[string length
$config(targetClass
)] ||
[string length
$config(Configuration
)]} {
1004 set config
(targetVendor
) $usb(idVendor
)
1005 set config
(targetProduct
) $usb(idProduct
)
1007 Log
"Check success of mode switch for max. $config(checkSuccess) seconds ..."
1010 for {set i
1} {$i <= $config(checkSuccess
)} {incr i
} {
1012 if {![file isdirectory
$devdir]} {
1013 Log
" Wait for device file system ($i sec.) ..."
1016 Log
" Read attributes ..."
1019 if {$ifdir == ""} {continue}
1020 set ifdir
[file tail
$ifdir]
1021 if {![ReadUSBAttrs
$devdir $ifdir]} {
1022 Log
" Essential attributes are missing, continue wait ..."
1025 if [string length
$config(Configuration
)] {
1026 if {$usb(bConfigurationValue
) != $config(Configuration
)} {continue}
1028 if [string length
$config(targetClass
)] {
1029 if {![regexp $usb($ifdir/bInterfaceClass
) $config(targetClass
)]} {
1030 if {$config(class
) != $usb($ifdir/bInterfaceClass
} {
1035 if {![regexp $usb(idVendor
) $config(targetVendor
)]} {
1036 if {![regexp $usb(idVendor
) $config(vendor
)]} {
1040 if {![regexp $usb(idProduct
) $config(targetProduct
)]} {
1041 if {![regexp $usb(idProduct
) $config(product
)]} {
1046 Log
" All attributes matched"
1048 if [regexp -nocase {/[0-9a-f
]+:#} $flags(config)] {
1049 Log
" idProduct has changed after generic mode-switch, assume success"
1051 Log
" Attributes are different but target values are unexpected:"
1063 # end of proc {CheckSuccess}
1066 proc {IfDir
} {iface
} {
1069 set allfiles
[glob -nocomplain $devdir/*]
1070 set files
[glob -nocomplain $devdir/*.
$iface]
1071 if {[llength $files] == 0} {
1074 set ifdir
[lindex $files 0]
1075 if {![file isdirectory
$ifdir]} {
1081 # end of proc {IfDir}
1083 proc {IfClass
} {iface
} {
1085 set ifdir
[IfDir
$iface]
1087 if {![file exists
$ifdir/bInterfaceClass
]} {
1090 set rc
[open $ifdir/bInterfaceClass r
]
1093 return [string trim
$c]
1096 # end of proc {IfClass}
1099 proc {SysLog
} {msg
} {
1102 if {![info exists flags
(logger
)]} {
1103 set flags
(logger
) ""
1104 foreach fn
{/bin
/logger
/usr
/bin
/logger
} {
1105 if [file exists
$fn] {
1106 set flags
(logger
) $fn
1109 Log
"Logger is $flags(logger)"
1111 if {$flags(logger
) == ""} {
1112 Log
"Can't add system message, no syslog helper found"
1115 catch {exec $flags(logger
) -p syslog.notice
"$msg" 2>/dev
/null
}
1118 # end of proc {SysLog}
1120 proc {SetStorageDelay
} {secs
} {
1122 Log
"Adjust delay for USB storage devices ..."
1123 set attrib
/sys
/module
/usb_storage
/parameters
/delay_use
1124 if {![file exists
$attrib]} {
1125 Log
"Error: could not find delay_use attribute"
1128 if [catch {set ch
[open $attrib r
+]} err
] {
1129 Log
"Error: could not access delay_use attribute: $err"
1132 if {[read $ch] < $secs} {
1134 puts -nonewline $ch $secs
1135 Log
" Delay set to $secs seconds\n"
1137 Log
" Current value is higher than $secs. Leave it alone\n"
1142 # end of proc {SetStorageDelay}
1144 proc {CheckMBIM
} {} {
1146 set kversion
[exec uname
-r]
1147 if [file exists
/lib
/modules
/$kversion/kernel
/drivers
/net
/usb
/cdc_mbim.ko
] {return 1}
1148 if [file exists
/sys
/bus
/usb
/drivers
/cdc_mbim
] {return 1}
1153 proc {LogAttributes
} {} {
1156 if $flags(logging
) {
1157 set attrList
{idVendor idProduct bConfigurationValue manufacturer product serial
}
1158 foreach attr
[lsort [array names usb
]] {
1159 Log
" [format %-26s $attr:] $usb($attr)"
1165 # The actual entry point