usb_modeswitch: ver. 1.1.9 with data package 2011-08-05
[tomato.git] / release / src / router / usbmodeswitch / usb_modeswitch.tcl
blobb9c8d30b9ceac28c85351ed00257ae7ae2dc6ffd
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-1.1.9 package
13 # (C) Josua Dietze 2009, 2010, 2011
16 # Setting of the these switches is done in the global config
17 # file (/etc/usb_modeswitch.conf) if available
19 set logging 0
20 set noswitching 0
23 set env(PATH) "/bin:/usr/bin"
25 # Execution starts at file bottom
27 proc {Main} {argc argv} {
29 global scsi usb config match wc device logging noswitching settings
31 set loginit [ParseGlobalConfig]
33 # The facility to add a symbolic link pointing to the
34 # ttyUSB port which provides interrupt transfer, i.e.
35 # the port to connect through; returns a symlink name
36 # for udev and exits
37 # This is run once for every known device interface by
38 # an udev rule
40 if {[lindex $argv 0] == "--symlink-name"} {
41 if $logging {
42 set device [clock clicks]
44 puts -nonewline [SymLinkName [lindex $argv 1]]
45 SafeExit
48 set settings(dbdir) /usr/share/usb_modeswitch
49 set settings(dbdir_etc) /etc/usb_modeswitch.d
51 if {![file exists $settings(dbdir)] && ![file exists $settings(dbdir_etc)]} {
52 set device "noname"
53 Log "Error: no config database found in /usr/share or /etc. Exiting"
54 SafeExit
56 set bindir /usr/sbin
58 set devList1 {}
59 set devList2 {}
61 # argv contains the values provided from the udev rule
62 # separated by "/"
64 set argList [split [lindex $argv 0] /]
66 if [string length [lindex $argList 1]] {
67 set device [lindex $argList 1]
68 } else {
69 set device "noname"
72 Log "Raw args from udev: $argv\n\n$loginit"
74 if {$device == "noname"} {
75 Log "No data from udev. Exiting"
76 SafeExit
79 # arg 0: the bus id for the device (udev: %b)
80 # arg 1: the "kernel name" for the device (udev: %k)
82 # Both together give the top directory where the path
83 # to the SCSI attributes can be determined (further down)
84 # Addendum: older kernel/udev version seem to differ in
85 # providing these attributes - or not. So more probing
86 # is needed
88 if {[string length [lindex $argList 0]] == 0} {
89 if {[string length [lindex $argList 1]] == 0} {
90 Log "No device number values given from udev! Exiting"
91 SafeExit
92 } else {
93 Log "Bus ID for device not given by udev."
94 Log " Trying to determine it from kernel name ([lindex $argList 1]) ..."
95 if {![regexp {(.*?):} [lindex $argList 1] d dev_top]} {
96 Log "Could not determine top device dir from udev values! Exiting"
97 SafeExit
100 } else {
101 set dev_top [lindex $argList 0]
102 regexp {(.*?):} $dev_top d dev_top
106 set devdir /sys/bus/usb/devices/$dev_top
107 if {![file isdirectory $devdir]} {
108 Log "Top sysfs directory not found ($devdir)! Exiting"
109 SafeExit
113 # Mapping of the short string identifiers (in the config
114 # file names) to the long name used here
116 # If we need them it's a snap to add new attributes here!
118 set match(sVe) scsi(vendor)
119 set match(sMo) scsi(model)
120 set match(sRe) scsi(rev)
121 set match(uMa) usb(manufacturer)
122 set match(uPr) usb(product)
123 set match(uSe) usb(serial)
126 # Now reading the USB attributes
128 ReadUSBAttrs $devdir
130 if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
131 Log "USB IDs not found in sysfs tree. Exiting"
132 SafeExit
135 Log "----------------\nUSB values from sysfs:"
136 foreach attr {manufacturer product serial} {
137 Log " $attr\t$usb($attr)"
139 Log "----------------"
141 if $noswitching {
142 Log "\nSwitching globally disabled. Exiting\n"
143 catch {exec logger -p syslog.notice "usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)" 2>/dev/null}
144 SafeExit
147 if {$usb(bNumConfigurations) == "1"} {
148 set configParam "-u -1"
149 Log "bNumConfigurations is 1 - don't check for active configuration"
150 } else {
151 set configParam ""
154 # Check if there is more than one config file for this USB ID,
155 # which would make an attribute test necessary. If so, check if
156 # SCSI values are needed
158 set configList [ConfigGet conflist $usb(idVendor):$usb(idProduct)]
160 if {[llength $configList] == 0} {
161 Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exiting"
162 SafeExit
165 set scsiNeeded 0
166 if {[llength $configList] > 1} {
167 if [regexp {:s} $configList] {
168 set scsiNeeded 1
171 if {!$scsiNeeded} {
172 Log "SCSI attributes not needed, moving on"
176 # Getting the SCSI values via libusb results in a detached
177 # usb-storage driver. Not good for devices that want to be
178 # left alone. Fortunately, the sysfs tree provides the values
179 # too without need for direct access
181 # First we wait until the SCSI data is ready - or timeout.
182 # Timeout means: no storage driver was bound to the device.
183 # We run 20 times max, every half second (max. 10 seconds
184 # total)
186 # We also check if the device itself changes, probably
187 # because it was switched by the kernel (or even unplugged).
188 # Then we do simply nothing and exit quietly ...
190 set counter 0
191 while {$scsiNeeded && $counter < 20} {
192 after 500
193 incr counter
194 Log "waiting for storage tree in sysfs"
196 set sysdir $devdir/[lindex $argList 1]
198 if {![file isdirectory $sysdir]} {
199 # Device is gone. Unplugged? Switched by kernel?
200 Log "sysfs device tree is gone; exiting"
201 SafeExit
203 set rc [open $devdir/product r]
204 set newproduct [read -nonewline $rc]
205 close $rc
206 if {![string match $newproduct $usb(product)]} {
207 # Device has just changed. Switched by someone else?
208 Log "device has changed; exiting"
209 SafeExit
212 # Searching the storage/SCSI tree; might take a while
213 if {[set dirList [glob -nocomplain $sysdir/host*]] != ""} {
214 set sysdir [lindex $dirList 0]
215 if {[set dirList [glob -nocomplain $sysdir/target*]] != ""} {
216 set sysdir [lindex $dirList 0]
217 regexp {.*target(.*)} $sysdir d subdir
218 if {[set dirList [glob -nocomplain $sysdir/$subdir*]] != ""} {
219 set sysdir [lindex $dirList 0]
220 if [file exists $sysdir/vendor] {
221 # Finally SCSI structure is ready, get the values
222 ReadSCSIAttrs $sysdir
223 Log "SCSI values read"
224 break
230 if $scsiNeeded {
231 if {$counter == 20 && [string length $scsi(vendor)] == 0} {
232 Log "SCSI tree not found; you may want to check if this path/file exists:"
233 Log "$sysdir/vendor\n"
234 } else {
235 Log "----------------\nSCSI values from sysfs:"
236 foreach attr {vendor model rev} {
237 Log " $attr\t$scsi($attr)"
239 Log "----------------"
241 Log "Waiting 3 secs. after SCSI device was added"
242 after 3000
243 } else {
244 # after 500
247 # If SCSI tree in sysfs was not identified, try and get the values
248 # from a (nonswitching) call of usb_modeswitch; this detaches the
249 # storage driver, so it's just the last resort
251 if {$scsiNeeded && $scsi(vendor)==""} {
252 set testSCSI [exec $bindir/usb_modeswitch -v 0x$usb(idVendor) -p 0x$usb(idProduct) 2>/dev/null]
253 regexp { Vendor String: (.*?)\n} $testSCSI d scsi(vendor)
254 regexp { Model String: (.*?)\n} $testSCSI d scsi(model)
255 regexp {Revision String: (.*?)\n} $testSCSI d scsi(rev)
256 Log "SCSI values from usb_modeswitch:"
257 foreach attr {vendor model rev} {
258 Log " $attr\t$scsi($attr)"
262 # If we don't have the SCSI values by now, we just
263 # leave the variables empty; they won't match anything
265 # Time to check for a matching config file.
266 # Matching itself is done by MatchDevice
268 # The configuration file names are sorted reverse so that
269 # the ones with matching additions are tried first; the
270 # common configs without match attributes are used at the
271 # end and provide a fallback
273 set report {}
274 foreach configuration $configList {
276 # skipping installer leftovers
277 if [regexp {\.(dpkg|rpm)} $configuration] {continue}
279 Log "checking config: $configuration"
280 if [MatchDevice $configuration] {
281 ParseDeviceConfig [ConfigGet conffile $configuration]
282 set devList1 [ListSerialDevs]
283 if {$config(waitBefore) == ""} {
284 Log "! matched, now switching"
285 } else {
286 Log "! matched, waiting time set to $config(waitBefore) seconds"
287 append config(waitBefore) "000"
288 after $config(waitBefore)
289 Log " waiting is over, switching starts now"
292 # Now we are actually switching
293 if $logging {
294 Log " (running command: $bindir/usb_modeswitch -I -W -c $settings(tmpConfig))"
295 set report [exec $bindir/usb_modeswitch -I -W -D -c $settings(tmpConfig) $configParam 2>@ stdout]
296 } else {
297 set report [exec $bindir/usb_modeswitch -I -Q -D -c $settings(tmpConfig) $configParam 2>/dev/null]
299 Log "\nVerbose debug output of usb_modeswitch and libusb follows"
300 Log "(Note that some USB errors are expected in the process)"
301 Log "--------------------------------"
302 Log $report
303 Log "--------------------------------"
304 Log "(end of usb_modeswitch output)\n"
305 if [regexp {/var/lib/usb_modeswitch} $settings(tmpConfig)] {
306 file delete $settings(tmpConfig)
308 break
309 } else {
310 Log "* no match, not switching with this config"
314 # We're finished with switching; success checking
315 # was done by usb_modeswitch and logged via syslog.
317 # If switching was OK we now check for drivers by
318 # simply recounting serial devices under /dev
320 if {![file isdirectory $devdir]} {
321 Log "Device directory in sysfs is gone! Something went wrong, aborting"
322 SafeExit
324 # Give the device annother second if it's not fully back yet
325 if {![file exists $devdir/idProduct]} {
326 after 1000
328 if {![file exists $devdir/idProduct]} {
329 after 1000
332 set ifdir "[file tail $devdir]:1.0"
333 ReadUSBAttrs $devdir $ifdir
335 if {$usb($ifdir/bInterfaceClass) != "" && [regexp {ok:} $report]} {
336 if {$usb($ifdir/bInterfaceClass) == "02"} {
337 set report "ok:"
338 Log " Found CDC ACM device, skip driver checking"
342 # If target ID given, driver shall be loaded
343 if [regexp -nocase {ok:[0-9a-f]{4}:[0-9a-f]{4}|ok:no_data} $report] {
345 if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
346 regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct)
348 set t "$usb(idVendor)$usb(idProduct)"
349 if {[string length $t] != 8 || [string trim $t 0] == ""} {
350 if {$report == "ok:no_data"} {
351 Log "Libusb1 bug prevented device searching, and device ID not found afterwards."
353 Log "No vendor/product ID found or given, can't continue. Aborting"
354 SafeExit
357 # For general driver loading; TODO: add respective device names.
358 # Presently only useful for HSO devices (which are recounted now)
359 if {$config(driverModule) == ""} {
360 set config(driverModule) "option"
361 set config(driverIDPath) "/sys/bus/usb-serial/drivers/option1"
362 } else {
363 if {$config(driverIDPath) == ""} {
364 set config(driverIDPath) "/sys/bus/usb/drivers/$config(driverModule)"
367 Log "Driver module is \"$config(driverModule)\", ID path is $config(driverIDPath)\n"
369 # some settling time in ms
370 after 500
372 Log "Now checking for newly created serial devices ..."
373 set devList2 [ListSerialDevs]
375 if {[llength $devList1] >= [llength $devList2]} {
376 Log " no new serial devices found"
377 AddToList link_list $usb(idVendor):$usb(idProduct)
379 # If device is known, the sh wrapper will take care, else:
380 if {[InBindList $usb(idVendor):$usb(idProduct)] == 0} {
381 Log "Device not in bind_list"
383 # Load driver
384 CheckDriverBind $usb(idVendor) $usb(idProduct)
385 set counter 0
387 # Old/slow systems may take a while to create the devices
388 while {[llength $devList1] >= [llength $devList2] && $counter < 14} {
389 after 500
390 set devList2 [ListSerialDevs]
391 incr counter
393 if {$counter == 14} {
394 Log " still no new serial devices found"
395 } else {
396 Log " driver successfully bound"
397 AddToList bind_list $usb(idVendor):$usb(idProduct)
400 } else {
401 Log " new serial devices found, driver has bound"
402 if {[llength [lsearch -glob -all $devList2 *ttyUSB*]] > [llength [lsearch -glob -all $devList1 *ttyUSB*]]} {
403 AddToList link_list $usb(idVendor):$usb(idProduct)
406 } else {
407 # Just in case "NoDriverLoading" was added after the first bind
408 RemoveFromBindList $usb(idVendor):$usb(idProduct)
411 if [regexp {ok:$} $report] {
412 # "NoDriverLoading" was set
413 Log "Doing no driver checking or binding for this device"
416 # In newer kernels there is a switch to avoid the use of a device
417 # reset (e.g. from usb-storage) which would possibly switch back
418 # a mode-switching device to initial mode
419 if [regexp {ok:} $report] {
420 Log "Checking for AVOID_RESET_QUIRK attribute"
421 if [file exists $devdir/avoid_reset_quirk] {
422 if [catch {exec echo "1" >$devdir/avoid_reset_quirk 2>/dev/null} err] {
423 Log " Error setting the attribute: $err"
424 } else {
425 Log " AVOID_RESET_QUIRK activated"
427 } else {
428 Log " AVOID_RESET_QUIRK not present"
432 Log "\nAll done, exiting\n"
433 SafeExit
436 # end of proc {Main}
439 proc {ReadSCSIAttrs} {dir} {
441 global scsi
442 Log "SCSI dir exists: $dir"
444 foreach attr {vendor model rev} {
445 if [file exists $dir/$attr] {
446 set rc [open $dir/$attr r]
447 set scsi($attr) [read -nonewline $rc]
448 close $rc
449 } else {
450 set scsi($attr) ""
451 Log "Warning: SCSI attribute \"$attr\" not found."
456 # end of proc {ReadSCSIAttrs}
459 proc {ReadUSBAttrs} {dir args} {
461 global usb
463 set attrList {idVendor idProduct manufacturer product serial bNumConfigurations}
464 if {$args != ""} {
465 lappend attrList "$args/bInterfaceClass"
467 foreach attr $attrList {
468 if [file exists $dir/$attr] {
469 set rc [open $dir/$attr r]
470 set usb($attr) [read -nonewline $rc]
471 close $rc
472 } else {
473 set usb($attr) ""
474 Log "Warning: USB attribute \"$attr\" not found."
479 # end of proc {ReadUSBAttrs}
482 proc {MatchDevice} {config} {
484 global scsi usb match
486 set devinfo [file tail $config]
487 set infoList [split $devinfo :]
488 set stringList [lrange $infoList 2 end]
489 if {[llength $stringList] == 0} {return 1}
491 foreach teststring $stringList {
492 if {$teststring == "?"} {return 0}
493 set tokenList [split $teststring =]
494 set id [lindex $tokenList 0]
495 set matchstring [lindex $tokenList 1]
496 set blankstring ""
497 regsub -all {_} $matchstring { } blankstring
498 Log "matching $match($id)"
499 Log " match string1 (exact): $matchstring"
500 Log " match string2 (blanks): $blankstring"
501 Log " device string: [set $match($id)]"
502 if {!([string match *$matchstring* [set $match($id)]] || [string match *$blankstring* [set $match($id)]])} {
503 return 0
506 return 1
509 # end of proc {MatchDevice}
512 proc {ParseGlobalConfig} {} {
514 global logging noswitching
516 set configFile ""
517 set places [list /etc/usb_modeswitch.conf /etc/sysconfig/usb_modeswitch /etc/default/usb_modeswitch]
518 foreach cfg $places {
519 if [file exists $cfg] {
520 set configFile $cfg
521 break
525 if {$configFile == ""} {return}
527 set rc [open $configFile r]
528 while {![eof $rc]} {
529 gets $rc line
530 if [regexp {DisableSwitching\s*=\s*([^\s]+)} $line d val] {
531 if [regexp -nocase {1|yes|true} $val] {
532 set noswitching 1
535 if [regexp {EnableLogging\s*=\s*([^\s]+)} $line d val] {
536 if [regexp -nocase {1|yes|true} $val] {
537 set logging 1
542 return "Using global config file: $configFile"
545 # end of proc {ParseGlobalConfig}
548 proc ParseDeviceConfig {configFile} {
550 global config
551 set config(driverModule) ""
552 set config(driverIDPath) ""
553 set config(waitBefore) ""
554 set rc [open $configFile r]
555 set lineList [split [read $rc] \n]
556 close $rc
557 foreach line $lineList {
558 if [regexp {^DriverModule[[:blank:]]*=[[:blank:]]*"?(\w+)"?} [string trim $line] d config(driverModule)] {
559 Log "config: DriverModule set to $config(driverModule)"
561 if [regexp {^DriverIDPath[[:blank:]]*=[[:blank:]]*?"?([/\-\w]+)"?} [string trim $line] d config(driverIDPath)] {
562 Log "config: DriverIDPath set to $config(driverIDPath)"
564 if [regexp {^WaitBefore[[:blank:]]*=[[:blank:]]*?([0-9]+)} [string trim $line] d config(waitBefore)] {
565 Log "config: WaitBefore set to $config(waitBefore)"
568 set config(waitBefore) [string trimleft $config(waitBefore) 0]
571 # end of proc {ParseDeviceConfig}
574 proc ConfigGet {command config} {
576 global settings
578 switch $command {
580 conflist {
581 # Unpackaged configs first; sorting is essential for priority
582 set configList [lsort -decreasing [glob -nocomplain $settings(dbdir_etc)/$config*]]
583 set configList [concat $configList [lsort -decreasing [glob -nocomplain $settings(dbdir)/$config*]]]
584 if [file exists $settings(dbdir)/configPack.tar.gz] {
585 Log "Found packed config collection $settings(dbdir)/configPack.tar.gz"
586 if [catch {set packedList [exec tar -tzf $settings(dbdir)/configPack.tar.gz 2>/dev/null]} err] {
587 Log "Error: problem opening config package; tar returned\n $err"
588 return {}
590 set packedList [split $packedList \n]
591 set packedConfigList [lsort -decreasing [lsearch -glob -all -inline $packedList $config*]]
592 # Now add packaged configs with a mark, again sorted for priority
593 foreach packedConfig $packedConfigList {
594 lappend configList "pack/$packedConfig"
598 return $configList
600 conffile {
601 if [regexp {^pack/} $config] {
602 set config [regsub {pack/} $config {}]
603 set settings(tmpConfig) /var/lib/usb_modeswitch/current_cfg
604 Log "Extracting config $config from collection $settings(dbdir)/configPack.tar.gz"
605 set wc [open $settings(tmpConfig) w]
606 puts -nonewline $wc [exec tar -xzOf $settings(dbdir)/configPack.tar.gz $config 2>/dev/null]
607 close $wc
608 } else {
609 if [regexp [list $settings(dbdir_etc)] $config] {
610 Log "Using config file from override folder $settings(dbdir_etc)"
611 set syslog_text "usb_modeswitch: using overriding config file $config; make sure this is intended"
612 catch {exec logger -p syslog.notice $syslog_text 2>/dev/null}
613 set syslog_text "usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files"
614 catch {exec logger -p syslog.notice $syslog_text 2>/dev/null}
616 set settings(tmpConfig) $config
618 return $settings(tmpConfig)
623 # end of proc {ConfigGet}
625 proc {Log} {msg} {
627 global wc logging device
628 if {$logging == 0} {return}
629 if {![info exists wc]} {
630 if [catch {set wc [open /var/log/usb_modeswitch_$device w]} err] {
631 set wc "error"
632 return
634 puts $wc "\n\nUSB_ModeSwitch log from [clock format [clock seconds]]\n"
636 if {$wc == "error"} {return}
637 puts $wc $msg
640 # end of proc {Log}
643 # Closing the log file if open and exit
644 proc {SafeExit} {} {
646 global wc
647 if [info exists wc] {
648 catch {close $wc}
650 exit
653 # end of proc {SafeExit}
656 # Checking for interrupt endpoint in ttyUSB port (lowest if there is
657 # more than one); if found, check for unused "gsmmodem[n]" name.
658 # Link for first modem will be "gsmmodem", then "gsmmodem2" and up
660 proc {SymLinkName} {path} {
661 global device
663 set loginit "* called with --symlink-name: params $path *\n"
665 # Internal proc, used only here
666 proc {hasInterrupt} {ifDir} {
667 if {[llength [glob -nocomplain $ifDir/ttyUSB*]] == 0} {
668 Log " no ttyUSB interface - skip checking endpoints"
669 return 0
671 foreach epDir [glob -nocomplain $ifDir/ep_*] {
672 Log " in epDir $epDir ..."
673 if [file exists $epDir/type] {
674 set rc [open $epDir/type r]
675 set type [read $rc]
676 close $rc
677 if [regexp {Interrupt} $type] {
678 Log " $epDir has interrupt transfer type"
679 return 1
683 return 0
686 # In case the device path is returned as /class/tty/ttyUSB,
687 # we need to extract the USB device path from symlink "device"
688 set linkpath /sys$path/device
689 if [file exists $linkpath] {
690 if {[file type $linkpath] == "link"} {
691 set rawpath [file readlink $linkpath]
692 set trimpath [regsub -all {\.\./} $rawpath {}]
693 if [file isdirectory /sys/$trimpath] {
694 set path /$trimpath
700 if {![regexp {ttyUSB[0-9]+} $path myPort]} {
701 Log "$loginit\nCould not find port name in path\n $path. Aborting"
702 return ""
704 set device $myPort
705 Log "$loginit\nMy name is $myPort"
707 if {![regexp {usb[0-9]+/([0-9]+-[0-9]+)/} $path d dev_top]} {
708 Log "Could not find device directory in path\n $path. Aborting"
709 return ""
712 if {![regexp "\[0-9\]+\\.(\[0-9\]+)/$myPort" $path d myIf]} {
713 Log "Could not find interface number in path\n $path. Aborting"
714 return ""
717 if {![regexp "$dev_top:\[0-9\]" /sys$path ifRoot]} {
718 Log "Could not find interface number in path\n $path. Aborting"
719 return ""
722 set dirList [split $path /]
723 set idx [lsearch $dirList $dev_top]
725 set devDir /sys[join [lrange $dirList 0 $idx] /]
727 Log "My port is $myPort, my interface is $myIf
728 devDir: $devDir\n dev_top: $dev_top\nsysPath: /sys$path"
730 regexp "$devDir/$dev_top:\[0-9\]" /sys$path ifRoot
732 set ifDir $ifRoot.$myIf
734 Log "Checking my endpoints in $ifDir"
735 if [hasInterrupt $ifDir] {
736 Log "\n--> I am an interrupt port\n"
737 set rightPort 1
738 } else {
739 Log "\n--> I am not an interrupt port\n"
740 set rightPort 0
743 # Unfortunately, there are devices with more than one interrupt
744 # port. The assumption so far is that the lowest of these is
745 # right. Check all lower interfaces for annother one (if interface)
746 # is bigger than 0). If found, don't return any name.
748 if { $rightPort && ($myIf > 0) } {
749 Log "Looking for lower ports with interrupt endpoints"
750 for {set i 0} {$i < $myIf} {incr i} {
751 set ifDir $ifRoot.$i
752 Log " in ifDir $ifDir ..."
753 if [hasInterrupt $ifDir] {
754 Log "\n--> found an interrupt interface below me\n"
755 set rightPort 0
756 break
761 if {$rightPort == 0} {
762 Log "Return empty name and exit"
763 return ""
766 Log "\n--> No interrupt interface below me\n"
768 cd /dev
769 set idx 2
770 set symlinkName "gsmmodem"
771 while {$idx < 256} {
772 if {![file exists $symlinkName]} {
773 break
775 set symlinkName gsmmodem$idx
776 incr idx
778 Log "Return symlink name \"$symlinkName\" and exit"
779 return $symlinkName
782 # end of proc {SymLinkName}
785 # Load and bind (serial) driver
787 proc {CheckDriverBind} {vid pid} {
788 global config
790 set loader ""
791 if [file exists /sbin/modprobe] {
792 set loader /sbin/modprobe
793 } else {
794 Log " /sbin/modprobe not found"
797 set idfile $config(driverIDPath)/new_id
798 if {![file exists $idfile]} {
799 if {$loader == ""} {
800 Log "Can't do anymore without module loader; get \"modtools\"!"
801 return
803 Log "\nTrying to load driver \"$config(driverModule)\""
804 if [catch {set result [exec $loader -v $config(driverModule)]} err] {
805 Log " Running \"$loader $config(driverModule)\" gave an error:\n $err"
806 } else {
807 Log " Driver was loaded successfully:\n$result"
809 } else {
810 Log "Driver was loaded already"
812 set i 0
813 while {$i < 50} {
814 if [file exists $idfile] {
815 break
817 after 20
818 incr i
820 if {$i < 50} {
821 Log "Trying to add ID to driver \"$config(driverModule)\""
822 catch {exec logger -p syslog.notice "usb_modeswitch: adding device ID $vid:$pid to driver \"$config(driverModule)\"" 2>/dev/null}
823 catch {exec logger -p syslog.notice "usb_modeswitch: please report the device ID to the Linux USB developers!" 2>/dev/null}
824 if [catch {exec echo "$vid $pid" >$idfile} err] {
825 Log "Error adding ID to driver: $err"
826 } else {
827 Log " ID added to driver; check for new devices in /dev"
829 } else {
830 Log " \"$idfile\" not found, can't add ID to driver;\n check if kernel version is at least 2.6.27"
831 Log "Falling back to \"usbserial\""
832 set config(driverModule) usbserial
833 Log "\nTrying to unload driver \"$config(driverModule)\""
834 if [catch {exec $loader -r $config(driverModule)} err] {
835 Log " Running \"$loader $config(driverModule)\" gave an error:\n $err"
836 Log "Can't unload usbserial. No more fallbacks"
837 return
839 Log "\nTrying to load driver \"usbserial\" with device IDs"
840 if [catch {set result [exec $loader -v usbserial vendor=0x$vid product=0x$pid]} err] {
841 Log " Running \"$loader $config(driverModule)\" gave an error:\n $err"
842 } else {
843 Log " Driver was loaded successfully:\n$result"
848 # end of proc {CheckDriverBind}
851 # Check if USB ID is listed as needing driver binding
852 proc {InBindList} {id} {
854 set listfile /var/lib/usb_modeswitch/bind_list
855 if {![file exists $listfile]} {return 0}
856 set rc [open $listfile r]
857 set buffer [read $rc]
858 close $rc
859 if [string match *$id* $buffer] {
860 Log "Found $id in bind_list"
861 return 1
862 } else {
863 Log "No $id in bind_list"
864 return 0
868 # end of proc {InBindList}
870 # Add USB ID to list of devices needing later treatment
871 proc {AddToList} {name id} {
873 set listfile /var/lib/usb_modeswitch/$name
874 set oldlistfile /etc/usb_modeswitch.d/bind_list
876 if {($name == "bind_list") && [file exists $oldlistfile] && ![file exists $listfile]} {
877 if [catch {file rename $oldlistfile $listfile} err] {
878 Log "Error renaming the old bind list file ($err)"
879 return
883 if [file exists $listfile] {
884 set rc [open $listfile r]
885 set buffer [read $rc]
886 close $rc
887 if [string match *$id* $buffer] {
888 return
890 set idList [split [string trim $buffer] \n]
892 lappend idList $id
893 set buffer [join $idList "\n"]
894 if [catch {set wc [open $listfile w]}] {return}
895 puts $wc $buffer
896 close $wc
899 # end of proc {AddToList}
902 # Remove USB ID from bind list (NoDriverLoading is set)
903 proc {RemoveFromBindList} {id} {
905 set listfile /var/lib/usb_modeswitch/bind_list
906 if [file exists $listfile] {
907 set rc [open $listfile r]
908 set buffer [read $rc]
909 close $rc
910 set idList [split [string trim $buffer] \n]
911 } else {
912 return
914 set idx [lsearch $idList $id]
915 if {$idx > -1} {
916 set idList [lreplace $idList $idx $idx]
917 } else {
918 return
920 if {[llength $idList] == 0} {
921 file delete $listfile
922 return
924 set buffer [join $idList "\n"]
925 if [catch {set wc [open $listfile w]}] {return}
926 puts $wc $buffer
927 close $wc
930 # end of proc {RemoveFromBindList}
932 # Return a list with all relevant serial devices that are present
933 proc {ListSerialDevs} {} {
935 set devList [glob -nocomplain /dev/ttyUSB* /dev/ttyACM* /dev/ttyHS*]
936 if [file isdirectory /dev/tts] {
937 eval lappend devList [glob -nocomplain /dev/tts/*]
939 return $devList
942 # end of proc {ListSerialDevs}
945 # The actual entry point
946 Main $argc $argv