9
9
# the mode switching program with the matching parameter
10
10
# file from /usr/share/usb_modeswitch
12
# Part of usb-modeswitch-1.2.3 package
13
# (C) Josua Dietze 2009-2012
12
# Part of usb-modeswitch-2.1.1 package
13
# (C) Josua Dietze 2009-2014
15
15
set arg0 [lindex $argv 0]
16
16
if [regexp {\.tcl$} $arg0] {
27
27
set flags(logging) 0
28
28
set flags(noswitching) 0
30
#set env(PATH) "/bin:/sbin:/usr/bin:/usr/sbin"
29
set flags(stordelay) 0
32
31
# Execution starts at file bottom
34
33
proc {Main} {argv argc} {
36
global scsi usb config match device flags settings
35
global scsi usb config match device flags setup devdir loginit
38
set loginit [ParseGlobalConfig]
38
Log "[ParseGlobalConfig]"
40
40
# The facility to add a symbolic link pointing to the
41
41
# ttyUSB port which provides interrupt transfer, i.e.
55
set argList [split [lindex $argv 1] /]
57
if [string length [lindex $argList 1]] {
58
set device [lindex $argList 1]
55
if {[lindex $argv 0] == "--switch-systemd"} {
56
set device [string trim [lindex $argv 1] "/-"]
57
set device [regsub {/} $device "-"]
58
set argList [list "" $device]
59
Log "\nStarted via systemd"
61
if {[lindex $argv 0] == "--switch-upstart"} {
62
Log "\nStarted via upstart"
64
set argList [split [lindex $argv 1] /]
65
if [string length [lindex $argList 1]] {
66
set device [lindex $argList 1]
71
if {$flags(stordelay) > 0} {
72
SetStorageDelay $flags(stordelay)
63
Log "Raw args from udev: [lindex $argv 1]\n\n$loginit"
75
Log "Raw args from udev: [lindex $argv 1]\n"
65
77
if {$device == "noname"} {
66
Log "No data from udev. Exiting"
70
if {[lindex $argv 0] != "--switch-mode"} {
71
Log "No command given. Exiting"
75
if {![regexp /lib/udev/usb_modeswitch [lindex $argv 2]]} {
76
Log "Dispatcher was not run from call script. Exiting"
80
set settings(dbdir) /usr/share/usb_modeswitch
81
set settings(dbdir_etc) /etc/usb_modeswitch.d
84
if {![file exists $settings(dbdir)] && ![file exists $settings(dbdir_etc)]} {
85
Log "Error: no config database found in /usr/share or /etc. Exiting"
78
Log "\nNo data from udev. Exit"
82
if {![regexp -- {--switch-} [lindex $argv 0]]} {
83
Log "\nNo command given. Exit"
87
set setup(dbdir) /usr/share/usb_modeswitch
88
set setup(dbdir_etc) /etc/usb_modeswitch.d
91
if {![file exists $setup(dbdir)] && ![file exists $setup(dbdir_etc)]} {
92
Log "\nError: no config database found in /usr/share or /etc. Exit"
88
95
set bindir /usr/sbin
94
# arg 0: the bus id for the device (udev: %b)
101
# arg 0: the bus id for the device (udev: %b), often ommitted
95
102
# arg 1: the "kernel name" for the device (udev: %k)
97
# Both together give the top directory where the path
98
# to the SCSI attributes can be determined (further down)
99
# Addendum: older kernel/udev version seem to differ in
100
# providing these attributes - or not. So more probing
104
# Used to determine the top directory for the device in sysfs
103
107
if {[string length [lindex $argList 0]] == 0} {
104
108
if {[string length [lindex $argList 1]] == 0} {
105
Log "No device number values given from udev! Exiting"
109
Log "No device number values given from udev! Exit"
108
112
if {![regexp {(.*?):} [lindex $argList 1] d dev_top]} {
109
Log "Could not determine top device dir from udev values! Exiting"
113
if [regexp {([0-9]+-[0-9]+\.?[0-9]*.*)} [lindex $argList 1] d dev_top] {
114
# new udev rules file, got to check class of first interface
117
Log "Could not determine device dir from udev values! Exit"
118
127
set devdir /sys/bus/usb/devices/$dev_top
119
128
if {![file isdirectory $devdir]} {
120
Log "Top device directory not found ($devdir)! Exiting"
129
Log "Top device directory not found ($devdir)! Exit"
123
Log "Using top device dir $devdir"
124
set ifdir "[file tail $devdir]:1.0"
132
Log "Use top device dir $devdir"
136
Log "Check class of first interface ..."
137
set iclass [IfClass 0]
139
Log " No access to interface 0. Exit"
142
Log " Interface class is $iclass."
143
if {$iclass == 8 || $iclass == 3} {
145
Log "No install mode found. Aborting"
149
set ifdir [file tail [IfDir $iface]]
150
regexp {:([0-9]+\.[0-9]+)$} $ifdir d iface
152
set flags(logwrite) 1
127
154
# Mapping of the short string identifiers (in the config
128
155
# file names) to the long name used here
140
167
# Now reading the USB attributes
141
168
if {![ReadUSBAttrs $devdir]} {
142
Log "USB attributes not found in sysfs tree. Exiting"
169
Log "USB attributes not found in sysfs tree. Exit"
146
173
if $flags(logging) {
147
Log "----------------\nUSB values from sysfs:"
174
Log "\n----------------\nUSB values from sysfs:"
148
175
foreach attr {manufacturer product serial} {
149
176
Log " $attr\t$usb($attr)"
171
198
set configList [ConfigGet conflist $usb(idVendor):$usb(idProduct)]
173
200
if {[llength $configList] == 0} {
174
Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exiting"
201
Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exit"
185
if [ReadSCSIAttrs $devdir:1.0] {
212
if [ReadSCSIAttrs $devdir:$iface] {
186
213
Log "----------------\nSCSI values from sysfs:"
187
214
foreach attr {vendor model rev} {
188
215
Log " $attr\t$scsi($attr)"
192
219
Log "Could not get SCSI attributes, exclude devices with SCSI match"
195
Log "SCSI attributes not needed, moving on"
222
Log "SCSI attributes not needed, move on"
198
# General wait - this is important
225
# General wait - some devices need this
201
228
# Now check for a matching config file. Matching is done
207
234
# skipping installer leftovers
208
235
if [regexp {\.(dpkg|rpm)} $configuration] {continue}
210
Log "checking config: $configuration"
237
Log "Check config: $configuration"
211
238
if [MatchDevice $configuration] {
212
Log "! matched. Reading config data"
239
Log "! matched. Read config data"
213
240
if [string length $usb(busnum)] {
214
241
set busParam "-b [string trimleft $usb(busnum) 0]"
215
242
set devParam "-g [string trimleft $usb(devnum) 0]"
221
248
ParseDeviceConfig $configBuffer
222
249
if {$config(waitBefore) == ""} {
224
Log " waiting time set to $config(waitBefore) seconds"
251
Log "Delay time of $config(waitBefore) seconds"
225
252
append config(waitBefore) "000"
226
253
after $config(waitBefore)
227
Log " waiting is over, switching starts now"
254
Log " wait is over, start mode switch"
256
if {$config(noMBIMCheck)==0 && $usb(bNumConfigurations) > 1} {
257
Log "Device may have an MBIM configuration, check driver ..."
259
Log " driver for MBIM devices is available"
260
Log "Find MBIM configuration number ..."
261
if [catch {set cfgno [exec /usr/sbin/usb_modeswitch -j -Q $busParam $devParam -v $usb(idVendor) -p $usb(idProduct)]} err] {
262
Log "Error when trying to find MBIM configuration, switch to legacy modem mode"
264
set cfgno [string trim $cfgno]
266
set config(Configuration) $cfgno
267
set config(driverModule) ""
268
set configBuffer "Configuration=$cfgno"
270
Log " No MBIM configuration found, switch to legacy modem mode"
274
Log " no MBIM driver found, switch to legacy modem mode"
230
278
# Now we are actually switching
231
279
if $flags(logging) {
232
Log "Command to be run:\nusb_modeswitch -I -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f \$configBuffer"
233
set report [exec /usr/sbin/usb_modeswitch -I -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@ stdout]
280
Log "Command to be run:\nusb_modeswitch -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f \$configBuffer"
281
set report [exec /usr/sbin/usb_modeswitch -W -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@1]
234
282
Log "\nVerbose debug output of usb_modeswitch and libusb follows"
235
283
Log "(Note that some USB errors are to be expected in the process)"
236
284
Log "--------------------------------"
238
286
Log "--------------------------------"
239
287
Log "(end of usb_modeswitch output)\n"
241
set report [exec /usr/sbin/usb_modeswitch -I -Q -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@ stdout]
289
set report [exec /usr/sbin/usb_modeswitch -Q -D -s 20 $configParam $busParam $devParam -v $usb(idVendor) -p $usb(idProduct) -f "$configBuffer" 2>@1]
260
308
foreach attr [lsort [array names usb]] {
261
309
Log " [format %-26s $attr:] $usb($attr)"
263
Log "\nMode switching may have failed. Exiting\n"
311
Log "\nMode switching may have failed. Exit"
267
315
if {![file isdirectory $devdir]} {
268
Log "Device directory in sysfs is gone! Something went wrong, aborting"
316
Log "Device directory in sysfs is gone! Something went wrong, abort"
271
319
if {![regexp {ok:} $report]} {
272
Log "\nCore program reported switching failure. Exiting\n"
320
Log "\nCore program reported switching failure. Exit"
275
323
# Give the device another second if it's not fully back yet
282
330
# Now checking for bound drivers (only for class 0xff)
284
if {$usb($ifdir/bInterfaceClass) != "" && [regexp {ok:} $report]} {
332
if {$config(driverModule) != "" && $usb($ifdir/bInterfaceClass) != "" && [regexp {ok:} $report]} {
285
333
if {$usb($ifdir/bInterfaceClass) != "ff"} {
286
334
set config(driverModule) ""
287
Log " No vendor-specific class found, skip driver checking"
335
Log " No vendor-specific class found, skip driver check"
294
342
if {$config(driverModule) != ""} {
295
343
if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
296
344
if {![regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct)]} {
297
Log "No target vendor/product ID found or given, can't continue. Aborting"
345
Log "No target vendor/product ID found or given, can't continue. Abort"
301
349
# wait for any drivers to bind automatically
303
Log "Now checking for bound driver ..."
351
Log "Now check for bound driver ..."
304
352
if {![file exists $devdir/$ifdir/driver]} {
305
353
Log " no driver has bound to interface 0 yet"
306
354
AddToList link_list $usb(idVendor):$usb(idProduct)
340
388
if [regexp {ok:$} $report] {
341
389
# "NoDriverLoading" was set
342
Log "Doing no driver checking or binding for this device"
390
Log "No driver check or bind for this device"
345
393
# In newer kernels there is a switch to avoid the use of a device
346
394
# reset (e.g. from usb-storage) which would possibly switch back
347
395
# a mode-switching device to initial mode
348
396
if [regexp {ok:} $report] {
349
Log "Checking for AVOID_RESET_QUIRK kernel attribute"
397
Log "Check for AVOID_RESET_QUIRK kernel attribute"
350
398
if [file exists $devdir/avoid_reset_quirk] {
351
399
if [catch {exec echo "1" >$devdir/avoid_reset_quirk 2>/dev/null} err] {
352
400
Log " Error setting the attribute: $err"
405
Log "Reading SCSI values ..."
453
Log "Read SCSI values ..."
406
454
foreach attr {vendor model rev} {
407
455
if [file exists $sysdir/$attr] {
408
456
set rc [open $sysdir/$attr r]
466
514
set matchstring [lindex $tokenList 1]
467
515
set blankstring ""
468
516
regsub -all {_} $matchstring { } blankstring
469
Log "matching $match($id)"
470
Log " match string1 (exact): $matchstring"
471
Log " match string2 (blanks): $blankstring"
517
Log "match $match($id)"
518
Log " string1 (exact): $matchstring"
519
Log " string2 (blanks): $blankstring"
472
520
Log " device string: [set $match($id)]"
473
521
if {!([string match *$matchstring* [set $match($id)]] || [string match *$blankstring* [set $match($id)]])} {
506
555
set flags(logging) 1
558
if [regexp {SetStorageDelay\s*=\s*([^\s]+)} $line d val] {
559
if [regexp {\d+} $val] {
560
set flags(stordelay) $val
511
return "Using global config file: $configFile"
565
return "Use global config file: $configFile"
514
568
# end of proc {ParseGlobalConfig}
524
578
set config(targetProduct) ""
525
579
set config(targetClass) ""
526
580
set config(Configuration) ""
581
set config(noMBIMCheck) 0
527
582
set config(checkSuccess) 20
554
609
if [regexp -line {^[^#]*?WaitBefore.*?=.*?([0-9]+).*?$} $configContent d config(waitBefore)] {
555
610
Log "config: WaitBefore set to $config(waitBefore)"
612
if [regexp -line {^[^#]*?NoMBIMCheck.*?=.*?([0-9]+).*?$} $configContent d config(noMBIMCheck)] {
613
Log "config: noMBIMCheck set to $config(noMBIMCheck)"
557
615
if [regexp -line {^[^#]*?NoDriverLoading.*?=.*?(1|yes|true).*?$} $configContent] {
559
617
Log "config: NoDriverLoading is set to active"
583
641
proc ConfigGet {command config} {
587
645
switch $command {
590
648
# Unpackaged configs first; sorting is essential for priority
591
set configList [lsort -decreasing [glob -nocomplain $settings(dbdir_etc)/$config*]]
592
set configList [concat $configList [lsort -decreasing [glob -nocomplain $settings(dbdir)/$config*]]]
593
if [file exists $settings(dbdir)/configPack.tar.gz] {
594
Log "Found packed config collection $settings(dbdir)/configPack.tar.gz"
595
if [catch {set packedList [exec tar -tzf $settings(dbdir)/configPack.tar.gz 2>/dev/null]} err] {
649
set configList [lsort -decreasing [glob -nocomplain $setup(dbdir_etc)/$config*]]
650
set configList [concat $configList [lsort -decreasing [glob -nocomplain $setup(dbdir)/$config*]]]
651
if [file exists $setup(dbdir)/configPack.tar.gz] {
652
Log "Found packed config collection $setup(dbdir)/configPack.tar.gz"
653
if [catch {set packedList [exec tar -tzf $setup(dbdir)/configPack.tar.gz 2>/dev/null]} err] {
596
654
Log "Error: problem opening config package; tar returned\n $err"
610
668
if [regexp {^pack/} $config] {
611
669
set config [regsub {pack/} $config {}]
612
Log "Extracting config $config from collection $settings(dbdir)/configPack.tar.gz"
613
set configContent [exec tar -xzOf $settings(dbdir)/configPack.tar.gz $config 2>/dev/null]
670
Log "Extract config $config from collection $setup(dbdir)/configPack.tar.gz"
671
set configContent [exec tar -xzOf $setup(dbdir)/configPack.tar.gz $config 2>/dev/null]
615
if [regexp [list $settings(dbdir_etc)] $config] {
616
Log "Using config file from override folder $settings(dbdir_etc)"
617
SysLog "usb_modeswitch: using overriding config file $config; make sure this is intended"
673
if [regexp [list $setup(dbdir_etc)] $config] {
674
Log "Use config file from override folder $setup(dbdir_etc)"
675
SysLog "usb_modeswitch: use overriding config file $config; make sure this is intended"
618
676
SysLog "usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files"
620
678
set rc [open $config r]
631
689
proc {Log} {msg} {
691
global flags device loginit
634
693
if {$flags(logging) == 0} {return}
636
if {![info exists flags(wc)]} {
637
if [catch {set flags(wc) [open /var/log/usb_modeswitch_$device w]} err] {
638
if [catch {set flags(wc) [open /dev/console w]} err] {
639
set flags(wc) "error"
642
puts $flags(wc) "Error opening log file ($err), redirect to console"
695
if $flags(logwrite) {
696
if [string length $loginit] {
697
exec echo "\nUSB_ModeSwitch log from [clock format [clock seconds]]" >/var/log/usb_modeswitch_$device
698
exec echo "$loginit" >>/var/log/usb_modeswitch_$device
645
puts $flags(wc) "\n\nUSB_ModeSwitch log from [clock format [clock seconds]]\n"
701
exec echo $msg >>/var/log/usb_modeswitch_$device
703
append loginit "\n$msg"
647
if {$flags(wc) == "error"} {return}
651
707
# end of proc {Log}
654
# Closing the log file if open and exit
710
# Writing the log file and exit
655
711
proc {SafeExit} {} {
658
if [info exists flags(wc)] {
659
catch {close $flags(wc)}
714
set $flags(logwrite) 1
670
725
proc {hasInterrupt} {ifDir} {
671
726
if {[llength [glob -nocomplain $ifDir/ttyUSB*]] == 0} {
672
Log " no ttyUSB interface - skip checking endpoints"
727
Log " no ttyUSB interface - skip endpoint check"
675
730
foreach epDir [glob -nocomplain $ifDir/ep_*] {
676
731
set e [file tail $epDir]
677
Log " checking $e ..."
678
733
if [file exists $epDir/type] {
679
734
set rc [open $epDir/type r]
680
735
set type [read $rc]
698
753
set rawpath [file readlink $linkpath]
699
754
set trimpath [regsub -all {\.\./} $rawpath {}]
700
755
if [file isdirectory /sys/$trimpath] {
701
append loginit "\n Using path $path\n"
756
append loginit "\n Use path $path\n"
702
757
set path /$trimpath
716
771
Log "$loginit\nMy name is $myPort\n"
718
773
if {![regexp {(.*?[0-9]+)\.([0-9]+)/ttyUSB} /sys$path d ifRoot ifNum]} {
719
Log "Could not find interface in path\n $path. Aborting"
774
Log "Could not find interface in path\n $path. Abort"
723
778
set ifDir $ifRoot.$ifNum
725
Log "Checking my endpoints ...\n in $ifDir"
780
Log "Check my endpoints ...\n in $ifDir"
726
781
if [hasInterrupt $ifDir] {
727
782
Log "\n--> I am an interrupt port"
736
791
# possible lower interfaces
738
793
if { $rightPort && ($ifNum > 0) } {
739
Log "\nLooking for lower ports with interrupt endpoints"
794
Log "\nLook for lower ports with interrupt endpoints"
740
795
for {set i 0} {$i < $ifNum} {incr i} {
741
796
set ifDir $ifRoot.$i
742
797
Log " in ifDir $ifDir ..."
793
848
Log "Can't do anymore without module loader; get \"modtools\"!"
796
Log "\nTrying to load module \"$config(driverModule)\""
851
Log "\nTry to load module \"$config(driverModule)\""
797
852
if [catch {set result [exec $loader -v $config(driverModule)]} err] {
798
853
Log " Running \"$loader $config(driverModule)\" gave an error:\n $err"
814
Log "Trying to add ID to driver \"$config(driverModule)\""
815
SysLog "usb_modeswitch: adding device ID $vid:$pid to driver \"$config(driverModule)\""
869
Log "Try to add ID to driver \"$config(driverModule)\""
870
SysLog "usb_modeswitch: add device ID $vid:$pid to driver \"$config(driverModule)\""
816
871
SysLog "usb_modeswitch: please report the device ID to the Linux USB developers!"
817
872
if [catch {exec echo "$vid $pid ff" >$idfile} err] {
818
873
Log " Error adding ID to driver:\n $err"
823
878
Log " \"$idfile\" not found, check if kernel version is at least 2.6.27"
824
Log "Falling back to \"usbserial\""
879
Log "Fall back to \"usbserial\""
825
880
set config(driverModule) usbserial
826
Log "\nTrying to unload driver \"usbserial\""
881
Log "\nTry to unload driver \"usbserial\""
827
882
if [catch {exec $loader -r usbserial} err] {
828
883
Log " Running \"$loader -r usbserial\" gave an error:\n $err"
829
884
Log "No more fallbacks"
833
Log "\nTrying to load driver \"usbserial\" with device IDs"
888
Log "\nTry to load driver \"usbserial\" with device IDs"
834
889
if [catch {set result [exec $loader -v usbserial vendor=0x$vid product=0x$pid]} err] {
835
890
Log " Running \"$loader usbserial\" gave an error:\n $err"
924
979
# end of proc {RemoveFromBindList}
926
982
proc {CheckSuccess} {devdir} {
928
984
global config usb
929
set ifdir "[file tail $devdir]:1.0"
985
set ifdir [file tail [IfDir 0]]
931
987
if {[string length $config(targetClass)] || [string length $config(Configuration)]} {
932
988
set config(targetVendor) $usb(idVendor)
933
989
set config(targetProduct) $usb(idProduct)
935
Log "Checking success of mode switch for max. $config(checkSuccess) seconds ..."
991
Log "Check success of mode switch for max. $config(checkSuccess) seconds ..."
937
993
for {set i 1} {$i <= $config(checkSuccess)} {incr i} {
939
995
if {![file isdirectory $devdir]} {
940
Log " Waiting for device file system ($i sec.) ..."
996
Log " Wait for device file system ($i sec.) ..."
943
Log " Reading attributes ..."
999
Log " Read attributes ..."
1002
if {$ifdir == ""} {continue}
1003
set ifdir [file tail $ifdir]
945
1004
if {![ReadUSBAttrs $devdir $ifdir]} {
946
1005
Log " Essential attributes are missing, continue wait ..."
950
1009
if {![regexp $usb($ifdir/bInterfaceClass) $config(targetClass)]} {continue}
952
1011
if [string length $config(Configuration)] {
953
if {$usb(bConfigurationValue) != $config(Configuration} {continue}
1012
if {$usb(bConfigurationValue) != $config(Configuration)} {continue}
955
1014
if {![regexp $usb(idVendor) $config(targetVendor)]} {continue}
956
1015
if {![regexp $usb(idProduct) $config(targetProduct)]} {continue}
966
1025
# end of proc {CheckSuccess}
1028
proc {IfDir} {iface} {
1031
set allfiles [glob -nocomplain $devdir/*]
1032
set files [glob -nocomplain $devdir/*.$iface]
1033
if {[llength $files] == 0} {
1036
set ifdir [lindex $files 0]
1037
if {![file isdirectory $ifdir]} {
1043
# end of proc {IfDir}
1045
proc {IfClass} {iface} {
1047
set ifdir [IfDir $iface]
1049
if {![file exists $ifdir/bInterfaceClass]} {
1052
set rc [open $ifdir/bInterfaceClass r]
1055
return [string trimleft [string trim $c] 0]
1058
# end of proc {IfClass}
968
1061
proc {SysLog} {msg} {
987
1080
# end of proc {SysLog}
1082
proc {SetStorageDelay} {secs} {
1084
Log "Adjust delay for USB storage devices ..."
1085
set attrib /sys/module/usb_storage/parameters/delay_use
1086
if {![file exists $attrib]} {
1087
Log "Error: could not find delay_use attribute"
1090
if [catch {set ch [open $attrib r+]} err] {
1091
Log "Error: could not access delay_use attribute: $err"
1094
if {[read $ch] < $secs} {
1096
puts -nonewline $ch $secs
1097
Log " Delay set to $secs seconds\n"
1099
Log " Current value is higher than $secs. Leave it alone\n"
1104
# end of proc {SetStorageDelay}
1106
proc {CheckMBIM} {} {
1108
set kversion [exec uname -r]
1109
if [file exists /lib/modules/$kversion/kernel/drivers/net/usb/cdc_mbim.ko] {return 1}
1110
if [file exists /sys/bus/usb/drivers/cdc_mbim] {return 1}
991
1116
# The actual entry point