minidlna support now Samsung TV C550/C650 (thx amir909)
[tomato.git] / release / src / router / usbmodeswitch / usb_modeswitch.tcl
blob04ae4bded7206ab8bbf08363d427d6be13235c66
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.7 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 if {![file exists $settings(dbdir)]} {
50 # Old place available?
51 set settings(dbdir) /etc/usb_modeswitch.d
52 if {![file exists $settings(dbdir)]} {
53 set device "noname"
54 Log "Error: no config database found in /usr/share or /etc. Exiting"
55 SafeExit
58 set bindir /usr/sbin
60 set devList1 {}
61 set devList2 {}
63 # argv contains the values provided from the udev rule
64 # separated by "/"
66 set argList [split [lindex $argv 0] /]
68 if [string length [lindex $argList 1]] {
69 set device [lindex $argList 1]
70 } else {
71 set device "noname"
74 Log "raw args from udev: $argv\n\n$loginit"
76 if {$device == "noname"} {
77 Log "No data from udev. Exiting"
78 SafeExit
81 # arg 0: the bus id for the device (udev: %b)
82 # arg 1: the "kernel name" for the device (udev: %k)
84 # Both together give the top directory where the path
85 # to the SCSI attributes can be determined (further down)
86 # Addendum: older kernel/udev version seem to differ in
87 # providing these attributes - or not. So more probing
88 # is needed
90 if {[string length [lindex $argList 0]] == 0} {
91 if {[string length [lindex $argList 1]] == 0} {
92 Log "No device number values given from udev! Exiting"
93 SafeExit
94 } else {
95 Log "Bus ID for device not given by udev."
96 Log " Trying to determine it from kernel name ([lindex $argList 1]) ..."
97 if {![regexp {(.*?):} [lindex $argList 1] d dev_top]} {
98 Log "Could not determine top device dir from udev values! Exiting"
99 SafeExit
102 } else {
103 set dev_top [lindex $argList 0]
104 regexp {(.*?):} $dev_top d dev_top
108 set devdir /sys/bus/usb/devices/$dev_top
109 if {![file isdirectory $devdir]} {
110 Log "Top sysfs directory not found ($devdir)! Exiting"
111 SafeExit
115 # Mapping of the short string identifiers (in the config
116 # file names) to the long name used here
118 # If we need them it's a snap to add new attributes here!
120 set match(sVe) scsi(vendor)
121 set match(sMo) scsi(model)
122 set match(sRe) scsi(rev)
123 set match(uMa) usb(manufacturer)
124 set match(uPr) usb(product)
125 set match(uSe) usb(serial)
128 # Now reading the USB attributes
130 ReadUSBAttrs $devdir
132 if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
133 Log "USB IDs not found in sysfs tree. Exiting"
134 SafeExit
137 Log "----------------\nUSB values from sysfs:"
138 foreach attr {manufacturer product serial} {
139 Log " $attr\t$usb($attr)"
141 Log "----------------"
143 if $noswitching {
144 Log "\nSwitching globally disabled. Exiting\n"
145 catch {exec logger -p syslog.notice "usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)" 2>/dev/null}
146 SafeExit
149 # Check if there is more than one config file for this USB ID,
150 # which would point to a possible ambiguity. If so, check if
151 # SCSI values are needed
153 set configList [ConfigGet list $usb(idVendor):$usb(idProduct)]
155 if {[llength $configList] == 0} {
156 Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exiting"
157 SafeExit
160 set scsiNeeded 0
161 if {[llength $configList] > 1} {
162 if [regexp {:s} $configList] {
163 set scsiNeeded 1
166 if {!$scsiNeeded} {
167 Log "SCSI attributes not needed, moving on"
171 # Getting the SCSI values via libusb results in a detached
172 # usb-storage driver. Not good for devices that want to be
173 # left alone. Fortunately, the sysfs tree provides the values
174 # too without need for direct access
176 # First we wait until the SCSI data is ready - or timeout.
177 # Timeout means: no storage driver was bound to the device.
178 # We run 20 times max, every half second (max. 10 seconds
179 # total)
181 # We also check if the device itself changes, probably
182 # because it was switched by the kernel (or even unplugged).
183 # Then we do simply nothing and exit quietly ...
185 set counter 0
186 while {$scsiNeeded && $counter < 20} {
187 after 500
188 incr counter
189 Log "waiting for storage tree in sysfs"
191 set sysdir $devdir/[lindex $argList 1]
193 if {![file isdirectory $sysdir]} {
194 # Device is gone. Unplugged? Switched by kernel?
195 Log "sysfs device tree is gone; exiting"
196 SafeExit
198 set rc [open $devdir/product r]
199 set newproduct [read -nonewline $rc]
200 close $rc
201 if {![string match $newproduct $usb(product)]} {
202 # Device has just changed. Switched by someone else?
203 Log "device has changed; exiting"
204 SafeExit
207 # Searching the storage/SCSI tree; might take a while
208 if {[set dirList [glob -nocomplain $sysdir/host*]] != ""} {
209 set sysdir [lindex $dirList 0]
210 if {[set dirList [glob -nocomplain $sysdir/target*]] != ""} {
211 set sysdir [lindex $dirList 0]
212 regexp {.*target(.*)} $sysdir d subdir
213 if {[set dirList [glob -nocomplain $sysdir/$subdir*]] != ""} {
214 set sysdir [lindex $dirList 0]
215 if [file exists $sysdir/vendor] {
216 # Finally SCSI structure is ready, get the values
217 ReadSCSIAttrs $sysdir
218 Log "SCSI values read"
219 break
225 if $scsiNeeded {
226 if {$counter == 20 && [string length $scsi(vendor)] == 0} {
227 Log "SCSI tree not found; you may want to check if this path/file exists:"
228 Log "$sysdir/vendor\n"
229 } else {
230 Log "----------------\nSCSI values from sysfs:"
231 foreach attr {vendor model rev} {
232 Log " $attr\t$scsi($attr)"
234 Log "----------------"
236 Log "Waiting 3 secs. after SCSI device was added"
237 after 3000
238 } else {
239 after 500
242 # If SCSI tree in sysfs was not identified, try and get the values
243 # from a (nonswitching) call of usb_modeswitch; this detaches the
244 # storage driver, so it's just the last resort
246 if {$scsiNeeded && $scsi(vendor)==""} {
247 set testSCSI [exec $bindir/usb_modeswitch -v 0x$usb(idVendor) -p 0x$usb(idProduct) 2>/dev/null]
248 regexp { Vendor String: (.*?)\n} $testSCSI d scsi(vendor)
249 regexp { Model String: (.*?)\n} $testSCSI d scsi(model)
250 regexp {Revision String: (.*?)\n} $testSCSI d scsi(rev)
251 Log "SCSI values from usb_modeswitch:"
252 foreach attr {vendor model rev} {
253 Log " $attr\t$scsi($attr)"
257 # If we don't have the SCSI values by now, we just
258 # leave the variables empty; they won't match anything
260 # Time to check for a matching config file.
261 # Matching itself is done by MatchDevice
263 # Sorting the configuration file names reverse so that
264 # the ones with matching additions are tried first; the
265 # common configs without match attributes are used at the
266 # end and provide a fallback
268 set report {}
269 foreach configuration [lsort -decreasing $configList] {
271 # skipping installer leftovers
272 if [regexp {\.(dpkg|rpm)} $configuration] {continue}
274 Log "checking config: $configuration"
275 if [MatchDevice $configuration] {
276 ParseDeviceConfig [ConfigGet config $configuration]
277 set devList1 [ListSerialDevs]
278 if {$config(waitBefore) == ""} {
279 Log "! matched, now switching"
280 } else {
281 Log "! matched, waiting time set to $config(waitBefore) seconds"
282 append config(waitBefore) "000"
283 after $config(waitBefore)
284 Log " waiting is over, switching starts now"
287 # Now we are actually switching
288 if $logging {
289 Log " (running command: $bindir/usb_modeswitch -I -W -c $settings(tmpConfig))"
290 set report [exec $bindir/usb_modeswitch -I -W -D -c $settings(tmpConfig) 2>@ stdout]
291 } else {
292 set report [exec $bindir/usb_modeswitch -I -Q -D -c $settings(tmpConfig) 2>/dev/null]
294 Log "\nVerbose debug output of usb_modeswitch and libusb follows\n(Note that some USB errors are expected in the process)"
295 Log "--------------------------------"
296 Log $report
297 Log "--------------------------------"
298 Log "(end of usb_modeswitch output)\n"
299 if [regexp {/tmp/} $settings(tmpConfig)] {
300 file delete $settings(tmpConfig)
302 break
303 } else {
304 Log "* no match, not switching with this config"
308 # We're finished with switching; success checking
309 # was done by usb_modeswitch and logged via syslog.
311 # If switching was OK we now check for drivers by
312 # simply recounting serial devices under /dev
314 if {![file isdirectory $devdir]} {
315 Log "Device directory in sysfs is gone! Something went wrong, aborting"
316 SafeExit
318 # Give the device annother second if it's not fully back yet
319 if {![file exists $devdir/idProduct]} {
320 after 1000
322 if {![file exists $devdir/idProduct]} {
323 after 1000
325 ReadUSBAttrs $devdir
327 # If target ID given, driver shall be loaded
328 if [regexp -nocase {ok:[0-9a-f]{4}:[0-9a-f]{4}|ok:no_data} $report] {
330 if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
331 regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct)
333 set t "$usb(idVendor)$usb(idProduct)"
334 if {[string length $t] != 8 || [string trim $t 0] == ""} {
335 if {$report == "ok:no_data"} {
336 Log "Libusb1 bug prevented device searching, and device ID not found afterwards."
338 Log "No vendor/product ID found or given, can't continue. Aborting"
339 SafeExit
342 # For general driver loading; TODO: add respective device names.
343 # Presently only useful for HSO devices (which are recounted now)
344 if {$config(driverModule) == ""} {
345 set config(driverModule) "option"
346 set config(driverIDPath) "/sys/bus/usb-serial/drivers/option1"
347 } else {
348 if {$config(driverIDPath) == ""} {
349 set config(driverIDPath) "/sys/bus/usb/drivers/$config(driverModule)"
352 Log "Driver module is \"$config(driverModule)\", ID path is $config(driverIDPath)\n"
354 # some settling time in ms
355 after 500
357 Log "Now checking for newly created serial devices ..."
358 set devList2 [ListSerialDevs]
360 if {[llength $devList1] >= [llength $devList2]} {
361 Log " no new serial devices found"
362 AddToList link_list $usb(idVendor):$usb(idProduct)
364 # If device is known, the sh wrapper will take care, else:
365 if {[InBindList $usb(idVendor):$usb(idProduct)] == 0} {
366 Log "Device not in bind_list"
368 # Load driver
369 CheckDriverBind $usb(idVendor) $usb(idProduct)
370 set counter 0
372 # Old/slow systems may take a while to create the devices
373 while {[llength $devList1] >= [llength $devList2] && $counter < 14} {
374 after 500
375 set devList2 [ListSerialDevs]
376 incr counter
378 if {$counter == 14} {
379 Log " still no new serial devices found"
380 } else {
381 Log " driver successfully bound"
382 AddToList bind_list $usb(idVendor):$usb(idProduct)
385 } else {
386 Log " new serial devices found, driver has bound"
387 if {[llength [lsearch -glob -all $devList2 *ttyUSB*]] > [llength [lsearch -glob -all $devList1 *ttyUSB*]]} {
388 AddToList link_list $usb(idVendor):$usb(idProduct)
391 } else {
392 # Just in case "NoDriverLoading" was added after the first bind
393 RemoveFromBindList $usb(idVendor):$usb(idProduct)
396 if [regexp {ok:$} $report] {
397 # "NoDriverLoading" was set
398 Log "Doing no driver checking or binding for this device"
401 # In newer kernels there is a switch to avoid the use of a device
402 # reset (e.g. from usb-storage) which would likely switch back
403 # a mode-switching device
404 if [regexp {ok:} $report] {
405 Log "Checking for AVOID_RESET_QUIRK attribute"
406 if [file exists $devdir/avoid_reset_quirk] {
407 if [catch {exec echo "1" >$devdir/avoid_reset_quirk 2>/dev/null} err] {
408 Log " Error setting the attribute: $err"
409 } else {
410 Log " AVOID_RESET_QUIRK activated"
412 } else {
413 Log " AVOID_RESET_QUIRK not present"
417 Log "\nAll done, exiting\n"
418 SafeExit
421 # end of proc {Main}
424 proc {ReadSCSIAttrs} {dir} {
426 global scsi
427 Log "SCSI dir exists: $dir"
429 foreach attr {vendor model rev} {
430 if [file exists $dir/$attr] {
431 set rc [open $dir/$attr r]
432 set scsi($attr) [read -nonewline $rc]
433 close $rc
434 } else {
435 set scsi($attr) ""
436 Log "Warning: SCSI attribute \"$attr\" not found."
441 # end of proc {ReadSCSIAttrs}
444 proc {ReadUSBAttrs} {dir} {
446 global usb
447 Log "USB dir exists: $dir"
449 foreach attr {idVendor idProduct manufacturer product serial} {
450 if [file exists $dir/$attr] {
451 set rc [open $dir/$attr r]
452 set usb($attr) [read -nonewline $rc]
453 close $rc
454 } else {
455 set usb($attr) ""
456 Log "Warning: USB attribute \"$attr\" not found."
461 # end of proc {ReadUSBAttrs}
464 proc {MatchDevice} {config} {
466 global scsi usb match
468 set devinfo [file tail $config]
469 set infoList [split $devinfo :]
470 set stringList [lrange $infoList 2 end]
471 if {[llength $stringList] == 0} {return 1}
473 foreach teststring $stringList {
474 if {$teststring == "?"} {return 0}
475 set tokenList [split $teststring =]
476 set id [lindex $tokenList 0]
477 set matchstring [lindex $tokenList 1]
478 set blankstring ""
479 regsub -all {_} $matchstring { } blankstring
480 Log "matching $match($id)"
481 Log " match string1: $matchstring"
482 Log " match string2: $blankstring"
483 Log " device string: [set $match($id)]"
484 if {!([string match *$matchstring* [set $match($id)]] || [string match *$blankstring* [set $match($id)]])} {
485 return 0
488 return 1
491 # end of proc {MatchDevice}
494 proc {ParseGlobalConfig} {} {
496 global logging noswitching
498 set configFile ""
499 set places [list /etc/usb_modeswitch.conf /etc/sysconfig/usb_modeswitch /etc/default/usb_modeswitch]
500 foreach cfg $places {
501 if [file exists $cfg] {
502 set configFile $cfg
503 break
507 if {$configFile == ""} {return}
509 set rc [open $configFile r]
510 while {![eof $rc]} {
511 gets $rc line
512 if [regexp {DisableSwitching\s*=\s*([^\s]+)} $line d val] {
513 if [regexp -nocase {1|yes|true} $val] {
514 set noswitching 1
517 if [regexp {EnableLogging\s*=\s*([^\s]+)} $line d val] {
518 if [regexp -nocase {1|yes|true} $val] {
519 set logging 1
524 return "Using global config file: $configFile"
527 # end of proc {ParseGlobalConfig}
530 proc ParseDeviceConfig {configFile} {
532 global config
533 set config(driverModule) ""
534 set config(driverIDPath) ""
535 set config(waitBefore) ""
536 set rc [open $configFile r]
537 set lineList [split [read $rc] \n]
538 close $rc
539 foreach line $lineList {
540 if [regexp {^DriverModule[[:blank:]]*=[[:blank:]]*"?(\w+)"?} [string trim $line] d config(driverModule)] {
541 Log "config: DriverModule set to $config(driverModule)"
543 if [regexp {^DriverIDPath[[:blank:]]*=[[:blank:]]*?"?([/\-\w]+)"?} [string trim $line] d config(driverIDPath)] {
544 Log "config: DriverIDPath set to $config(driverIDPath)"
546 if [regexp {^WaitBefore[[:blank:]]*=[[:blank:]]*?([0-9]+)} [string trim $line] d config(waitBefore)] {
547 Log "config: WaitBefore set to $config(waitBefore)"
550 set config(waitBefore) [string trimleft $config(waitBefore) 0]
553 # end of proc {ParseDeviceConfig}
556 proc ConfigGet {command config} {
558 global settings
560 switch $command {
562 list {
563 if [file exists $settings(dbdir)/configPack.tar.gz] {
564 Log "Found packed config collection $settings(dbdir)/configPack.tar.gz"
565 if [catch {set configList [exec tar -tzf $settings(dbdir)/configPack.tar.gz 2>/dev/null]} err] {
566 Log "Error: problem opening config package; tar returned\n $err"
567 return {}
569 set configList [split $configList \n]
570 set configList [lsearch -glob -all -inline $configList $config*]
571 } else {
572 set configList [glob -nocomplain $settings(dbdir)/$config*]
575 return $configList
577 config {
578 if [file exists $settings(dbdir)/configPack.tar.gz] {
579 set settings(tmpConfig) /tmp/usb_modeswitch.current_cfg
580 Log "Extracting config $config from collection $settings(dbdir)/configPack.tar.gz"
581 set wc [open $settings(tmpConfig) w]
582 puts -nonewline $wc [exec tar -xzOf $settings(dbdir)/configPack.tar.gz $config 2>/dev/null]
583 close $wc
584 } else {
585 set settings(tmpConfig) $config
587 return $settings(tmpConfig)
592 # end of proc {ConfigGet}
594 proc {Log} {msg} {
596 global wc logging device
597 if {$logging == 0} {return}
598 if {![info exists wc]} {
599 if [catch {set wc [open /var/log/usb_modeswitch_$device w]} err] {
600 set wc "error"
601 return
603 puts $wc "\n\nUSB_ModeSwitch log from [clock format [clock seconds]]\n"
605 if {$wc == "error"} {return}
606 puts $wc $msg
609 # end of proc {Log}
612 # Closing the log file if open and exit
613 proc {SafeExit} {} {
615 global wc
616 if [info exists wc] {
617 catch {close $wc}
619 exit
622 # end of proc {SafeExit}
625 # Checking for interrupt endpoint in ttyUSB port (lowest if there is
626 # more than one); if found, check for unused "gsmmodem[n]" name.
627 # Link for first modem will be "gsmmodem", then "gsmmodem2" and up
629 proc {SymLinkName} {path} {
630 global device
632 set loginit "* called with --symlink-name: params $path *\n"
634 # Internal proc, used only here
635 proc {hasInterrupt} {ifDir} {
636 if {[llength [glob -nocomplain $ifDir/ttyUSB*]] == 0} {
637 Log " no ttyUSB interface - skip checking endpoints"
638 return 0
640 foreach epDir [glob -nocomplain $ifDir/ep_*] {
641 Log " in epDir $epDir ..."
642 if [file exists $epDir/type] {
643 set rc [open $epDir/type r]
644 set type [read $rc]
645 close $rc
646 if [regexp {Interrupt} $type] {
647 Log " $epDir has interrupt transfer type"
648 return 1
652 return 0
655 # In case the device path is returned as /class/tty/ttyUSB,
656 # we need to extract the USB device path from symlink "device"
657 set linkpath /sys$path/device
658 if [file exists $linkpath] {
659 if {[file type $linkpath] == "link"} {
660 set rawpath [file readlink $linkpath]
661 set trimpath [regsub -all {\.\./} $rawpath {}]
662 if [file isdirectory /sys/$trimpath] {
663 set path /$trimpath
669 if {![regexp {ttyUSB[0-9]+} $path myPort]} {
670 Log "$loginit\nCould not find port name in path\n $path. Aborting"
671 return ""
673 set device $myPort
674 Log "$loginit\nMy name is $myPort"
676 if {![regexp {usb[0-9]+/([0-9]+-[0-9]+)/} $path d dev_top]} {
677 Log "Could not find device directory in path\n $path. Aborting"
678 return ""
681 if {![regexp "\[0-9\]+\\.(\[0-9\]+)/$myPort" $path d myIf]} {
682 Log "Could not find interface number in path\n $path. Aborting"
683 return ""
686 if {![regexp "$dev_top:\[0-9\]" /sys$path ifRoot]} {
687 Log "Could not find interface number in path\n $path. Aborting"
688 return ""
691 set dirList [split $path /]
692 set idx [lsearch $dirList $dev_top]
694 set devDir /sys[join [lrange $dirList 0 $idx] /]
696 Log "My port is $myPort, my interface is $myIf
697 devDir: $devDir\n dev_top: $dev_top\nsysPath: /sys$path"
699 regexp "$devDir/$dev_top:\[0-9\]" /sys$path ifRoot
701 set ifDir $ifRoot.$myIf
703 Log "Checking my endpoints in $ifDir"
704 if [hasInterrupt $ifDir] {
705 Log "\n--> I am an interrupt port\n"
706 set rightPort 1
707 } else {
708 Log "\n--> I am not an interrupt port\n"
709 set rightPort 0
712 # Unfortunately, there are devices with more than one interrupt
713 # port. The assumption so far is that the lowest of these is
714 # right. Check all lower interfaces for annother one (if interface)
715 # is bigger than 0). If found, don't return any name.
717 if { $rightPort && ($myIf > 0) } {
718 Log "Looking for lower ports with interrupt endpoints"
719 for {set i 0} {$i < $myIf} {incr i} {
720 set ifDir $ifRoot.$i
721 Log " in ifDir $ifDir ..."
722 if [hasInterrupt $ifDir] {
723 Log "\n--> found an interrupt interface below me\n"
724 set rightPort 0
725 break
730 if {$rightPort == 0} {
731 Log "Return empty name and exit"
732 return ""
735 Log "\n--> No interrupt interface below me\n"
737 cd /dev
738 set idx 2
739 set symlinkName "gsmmodem"
740 while {$idx < 256} {
741 if {![file exists $symlinkName]} {
742 break
744 set symlinkName gsmmodem$idx
745 incr idx
747 Log "Return symlink name \"$symlinkName\" and exit"
748 return $symlinkName
751 # end of proc {SymLinkName}
754 # Load and bind (serial) driver
756 proc {CheckDriverBind} {vid pid} {
757 global config
759 set loader ""
760 if [file exists /sbin/modprobe] {
761 set loader /sbin/modprobe
762 } else {
763 Log " /sbin/modprobe not found"
766 set idfile $config(driverIDPath)/new_id
767 if {![file exists $idfile]} {
768 if {$loader == ""} {
769 Log "Can't do anymore without module loader; get \"modtools\"!"
770 return
772 Log "\nTrying to load driver \"$config(driverModule)\""
773 if [catch {set result [exec $loader -v $config(driverModule)]} err] {
774 Log " Running \"$loader $config(driverModule)\" gave an error:\n $err"
775 } else {
776 Log " Driver was loaded successfully:\n$result"
778 } else {
779 Log "Driver was loaded already"
781 set i 0
782 while {$i < 50} {
783 if [file exists $idfile] {
784 break
786 after 20
787 incr i
789 if {$i < 50} {
790 Log "Trying to add ID to driver \"$config(driverModule)\""
791 catch {exec logger -p syslog.notice "usb_modeswitch: adding device ID $vid:$pid to driver \"$config(driverModule)\"" 2>/dev/null}
792 if [catch {exec echo "$vid $pid" >$idfile} err] {
793 Log "Error adding ID to driver: $err"
794 } else {
795 Log " ID added to driver; check for new devices in /dev"
797 } else {
798 Log " \"$idfile\" not found, can't add ID to driver;\n check if kernel version is at least 2.6.27"
799 Log "Falling back to \"usbserial\""
800 set config(driverModule) usbserial
801 Log "\nTrying to unload driver \"$config(driverModule)\""
802 if [catch {exec $loader -r $config(driverModule)} err] {
803 Log " Running \"$loader $config(driverModule)\" gave an error:\n $err"
804 Log "Can't unload usbserial. No more fallbacks"
805 return
807 Log "\nTrying to load driver \"usbserial\" with device IDs"
808 if [catch {set result [exec $loader -v usbserial vendor=0x$vid product=0x$pid]} err] {
809 Log " Running \"$loader $config(driverModule)\" gave an error:\n $err"
810 } else {
811 Log " Driver was loaded successfully:\n$result"
816 # end of proc {CheckDriverBind}
819 # Check if USB ID is listed as needing driver binding
820 proc {InBindList} {id} {
822 set listfile /var/lib/usb_modeswitch/bind_list
823 if {![file exists $listfile]} {return 0}
824 set rc [open $listfile r]
825 set buffer [read $rc]
826 close $rc
827 if [string match *$id* $buffer] {
828 Log "Found $id in bind_list"
829 return 1
830 } else {
831 Log "No $id in bind_list"
832 return 0
836 # end of proc {InBindList}
838 # Add USB ID to list of devices needing later treatment
839 proc {AddToList} {name id} {
841 set listfile /var/lib/usb_modeswitch/$name
842 set oldlistfile /etc/usb_modeswitch.d/bind_list
844 if {($name == "bind_list") && [file exists $oldlistfile] && ![file exists $listfile]} {
845 if [catch {file rename $oldlistfile $listfile} err] {
846 Log "Error renaming the old bind list file ($err)"
847 return
851 if [file exists $listfile] {
852 set rc [open $listfile r]
853 set buffer [read $rc]
854 close $rc
855 if [string match *$id* $buffer] {
856 return
858 set idList [split [string trim $buffer] \n]
860 lappend idList $id
861 set buffer [join $idList "\n"]
862 if [catch {set wc [open $listfile w]}] {return}
863 puts $wc $buffer
864 close $wc
867 # end of proc {AddToList}
870 # Remove USB ID from bind list (NoDriverLoading is set)
871 proc {RemoveFromBindList} {id} {
873 set listfile /var/lib/usb_modeswitch/bind_list
874 if [file exists $listfile] {
875 set rc [open $listfile r]
876 set buffer [read $rc]
877 close $rc
878 set idList [split [string trim $buffer] \n]
879 } else {
880 return
882 set idx [lsearch $idList $id]
883 if {$idx > -1} {
884 set idList [lreplace $idList $idx $idx]
885 } else {
886 return
888 if {[llength $idList] == 0} {
889 file delete $listfile
890 return
892 set buffer [join $idList "\n"]
893 if [catch {set wc [open $listfile w]}] {return}
894 puts $wc $buffer
895 close $wc
898 # end of proc {RemoveFromBindList}
900 # Return a list with all relevant serial devices that are present
901 proc {ListSerialDevs} {} {
903 set devList [glob -nocomplain /dev/ttyUSB* /dev/ttyACM* /dev/ttyHS*]
904 if [file isdirectory /dev/tts] {
905 eval lappend devList [glob -nocomplain /dev/tts/*]
907 return $devList
910 # end of proc {ListSerialDevs}
913 # The actual entry point
914 Main $argc $argv