home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / sbin / usb_modeswitch_dispatcher < prev    next >
Encoding:
Tcl/Tk script  |  2012-02-24  |  25.9 KB  |  993 lines

  1. #!/usr/bin/tclsh
  2.  
  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
  7. #
  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
  11. #
  12. # Part of usb-modeswitch-1.2.3 package
  13. # (C) Josua Dietze 2009-2012
  14.  
  15. set arg0 [lindex $argv 0]
  16. if [regexp {\.tcl$} $arg0] {
  17.     if [file exists $arg0] {
  18.         set argv [lrange $argv 1 end]
  19.         source $arg0
  20.         exit
  21.     }
  22. }
  23.  
  24. # Setting of these switches is done in the global config
  25. # file (/etc/usb_modeswitch.conf) if available
  26.  
  27. set flags(logging) 0
  28. set flags(noswitching) 0
  29.  
  30. #set env(PATH) "/bin:/sbin:/usr/bin:/usr/sbin"
  31.  
  32. # Execution starts at file bottom
  33.  
  34. proc {Main} {argv argc} {
  35.  
  36. global scsi usb config match device flags settings
  37.  
  38. set loginit [ParseGlobalConfig]
  39.  
  40. # The facility to add a symbolic link pointing to the
  41. # ttyUSB port which provides interrupt transfer, i.e.
  42. # the port to connect through.
  43. # Will check for interrupt endpoint in ttyUSB port (lowest if
  44. # there is more than one); if found, return "gsmmodem[n]" name
  45. # to udev for symlink creation
  46.  
  47. # This is run once for every port of LISTED devices by
  48. # an udev rule
  49.  
  50. if {[lindex $argv 0] == "--symlink-name"} {
  51.     puts -nonewline [SymLinkName [lindex $argv 1]]
  52.     SafeExit
  53. }
  54.  
  55. set argList [split [lindex $argv 1] /]
  56.  
  57. if [string length [lindex $argList 1]] {
  58.     set device [lindex $argList 1]
  59. } else {
  60.     set device "noname"
  61. }
  62.  
  63. Log "Raw args from udev: [lindex $argv 1]\n\n$loginit"
  64.  
  65. if {$device == "noname"} {
  66.     Log "No data from udev. Exiting"
  67.     SafeExit
  68. }
  69.  
  70. if {[lindex $argv 0] != "--switch-mode"} {
  71.     Log "No command given. Exiting"
  72.     SafeExit
  73. }
  74.  
  75. if {![regexp /lib/udev/usb_modeswitch [lindex $argv 2]]} {
  76.     Log "Dispatcher was not run from call script. Exiting"
  77.     SafeExit
  78. }
  79.  
  80. set settings(dbdir)    /usr/share/usb_modeswitch
  81. set settings(dbdir_etc)    /etc/usb_modeswitch.d
  82.  
  83.  
  84. if {![file exists $settings(dbdir)] && ![file exists $settings(dbdir_etc)]} {
  85.     Log "Error: no config database found in /usr/share or /etc. Exiting"
  86.     SafeExit
  87. }
  88. set bindir /usr/sbin
  89.  
  90. set devList1 {}
  91. set devList2 {}
  92.  
  93.  
  94. # arg 0: the bus id for the device (udev: %b)
  95. # arg 1: the "kernel name" for the device (udev: %k)
  96. #
  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
  101. # is needed
  102.  
  103. if {[string length [lindex $argList 0]] == 0} {
  104.     if {[string length [lindex $argList 1]] == 0} {
  105.         Log "No device number values given from udev! Exiting"
  106.         SafeExit
  107.     } else {
  108.         if {![regexp {(.*?):} [lindex $argList 1] d dev_top]} {
  109.             Log "Could not determine top device dir from udev values! Exiting"
  110.             SafeExit
  111.         }
  112.     }
  113. } else {
  114.     set dev_top [lindex $argList 0]
  115.     regexp {(.*?):} $dev_top d dev_top
  116. }
  117.  
  118. set devdir /sys/bus/usb/devices/$dev_top
  119. if {![file isdirectory $devdir]} {
  120.     Log "Top device directory not found ($devdir)! Exiting"
  121.     SafeExit
  122. }
  123. Log "Using top device dir $devdir"
  124. set ifdir "[file tail $devdir]:1.0"
  125.  
  126.  
  127. # Mapping of the short string identifiers (in the config
  128. # file names) to the long name used here
  129. #
  130. # If we need them it's a snap to add new attributes here!
  131.  
  132. set match(sVe) scsi(vendor)
  133. set match(sMo) scsi(model)
  134. set match(sRe) scsi(rev)
  135. set match(uMa) usb(manufacturer)
  136. set match(uPr) usb(product)
  137. set match(uSe) usb(serial)
  138.  
  139.  
  140. # Now reading the USB attributes
  141. if {![ReadUSBAttrs $devdir]} {
  142.     Log "USB attributes not found in sysfs tree. Exiting"
  143.     SafeExit
  144. }
  145.  
  146. if $flags(logging) {
  147.     Log "----------------\nUSB values from sysfs:"
  148.     foreach attr {manufacturer product serial} {
  149.         Log "  $attr\t$usb($attr)"
  150.     }
  151.     Log "----------------"
  152. }
  153.  
  154. if $flags(noswitching) {
  155.     Log "\nSwitching globally disabled. Exiting\n"
  156.     SysLog "usb_modeswitch: switching disabled, no action for $usb(idVendor):$usb(idProduct)"
  157.     SafeExit
  158. }
  159.  
  160. if {$usb(bNumConfigurations) == "1"} {
  161.     set configParam "-u -1"
  162.     Log "bNumConfigurations is 1 - don't check for active configuration"
  163. } else {
  164.     set configParam ""
  165. }
  166.  
  167. # Check if there is more than one config file for this USB ID,
  168. # which would make an attribute test necessary. If so, check if
  169. # SCSI values are needed
  170.  
  171. set configList [ConfigGet conflist $usb(idVendor):$usb(idProduct)]
  172.  
  173. if {[llength $configList] == 0} {
  174.     Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exiting"
  175.     SafeExit
  176. }
  177.  
  178. set scsiNeeded 0
  179. if {[llength $configList] > 1} {
  180.     if [regexp {:s} $configList] {
  181.         set scsiNeeded 1
  182.     }
  183. }
  184. if $scsiNeeded {
  185.     if [ReadSCSIAttrs $devdir:1.0] {
  186.         Log "----------------\nSCSI values from sysfs:"
  187.         foreach attr {vendor model rev} {
  188.             Log " $attr\t$scsi($attr)"
  189.         }
  190.         Log "----------------"
  191.     } else {
  192.         Log "Could not get SCSI attributes, exclude devices with SCSI match"
  193.     }
  194. } else {
  195.     Log "SCSI attributes not needed, moving on"
  196. }
  197.  
  198. # General wait - this is important
  199. after 500
  200.  
  201. # Now check for a matching config file. Matching is done
  202. # by MatchDevice
  203.  
  204. set report {}
  205. foreach configuration $configList {
  206.  
  207.     # skipping installer leftovers
  208.     if [regexp {\.(dpkg|rpm)} $configuration] {continue}
  209.  
  210.     Log "checking config: $configuration"
  211.     if [MatchDevice $configuration] {
  212.         Log "! matched. Reading config data"
  213.         if [string length $usb(busnum)] {
  214.             set busParam "-b [string trimleft $usb(busnum) 0]"
  215.             set devParam "-g [string trimleft $usb(devnum) 0]"
  216.         } else {
  217.             set busParam ""
  218.             set devParam ""
  219.         }
  220.         set configBuffer [ConfigGet conffile $configuration]
  221.         ParseDeviceConfig $configBuffer
  222.         if {$config(waitBefore) == ""} {
  223.         } else {
  224.             Log " waiting time set to $config(waitBefore) seconds"
  225.             append config(waitBefore) "000"
  226.             after $config(waitBefore)
  227.             Log " waiting is over, switching starts now"
  228.         }
  229.  
  230.         # Now we are actually switching
  231.         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]
  234.             Log "\nVerbose debug output of usb_modeswitch and libusb follows"
  235.             Log "(Note that some USB errors are to be expected in the process)"
  236.             Log "--------------------------------"
  237.             Log $report
  238.             Log "--------------------------------"
  239.             Log "(end of usb_modeswitch output)\n"
  240.         } else {
  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]
  242.         }
  243.         break
  244.     } else {
  245.         Log "* no match, not switching with this config"
  246.     }
  247. }
  248.  
  249. # Switching is complete; success checking was either
  250. # done by usb_modeswitch and logged via syslog OR bus/dev
  251. # parameter were used; then we do check for success HERE
  252.  
  253. if [regexp {ok:busdev} $report] {
  254.     if [CheckSuccess $devdir] {
  255.         Log "Mode switching was successful, found $usb(idVendor):$usb(idProduct) ($usb(manufacturer): $usb(product))"
  256.         SysLog "usb_modeswitch: switched to $usb(idVendor):$usb(idProduct) on [format %03d $usb(busnum)]/[format %03d $usb(devnum)]"
  257.     } else {
  258.         Log "\nTarget config not matching - current values are"
  259.         set attrList {idVendor idProduct bConfigurationValue manufacturer product serial}
  260.         foreach attr [lsort [array names usb]] {
  261.             Log "    [format %-26s $attr:] $usb($attr)"
  262.         }
  263.         Log "\nMode switching may have failed. Exiting\n"
  264.         SafeExit
  265.     }
  266. } else {
  267.     if {![file isdirectory $devdir]} {
  268.         Log "Device directory in sysfs is gone! Something went wrong, aborting"
  269.         SafeExit
  270.     }
  271.     if {![regexp {ok:} $report]} {
  272.         Log "\nCore program reported switching failure. Exiting\n"
  273.         SafeExit
  274.     }
  275.     # Give the device another second if it's not fully back yet
  276.     if {![file exists $devdir/idProduct]} {
  277.         after 1000
  278.     }
  279.     ReadUSBAttrs $devdir $ifdir
  280. }
  281.  
  282. # Now checking for bound drivers (only for class 0xff)
  283.  
  284. if {$usb($ifdir/bInterfaceClass) != "" && [regexp {ok:} $report]} {
  285.     if {$usb($ifdir/bInterfaceClass) != "ff"} {
  286.         set config(driverModule) ""
  287.         Log " No vendor-specific class found, skip driver checking"
  288.     }
  289. }
  290.  
  291. # If module is set (it is by default), driver shall be loaded.
  292. # If not, then NoDriverLoading is active
  293.  
  294. if {$config(driverModule) != ""} {
  295.     if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
  296.         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"
  298.             SafeExit
  299.         }
  300.     }
  301.     # wait for any drivers to bind automatically
  302.     after 1000
  303.     Log "Now checking for bound driver ..."
  304.     if {![file exists $devdir/$ifdir/driver]} {
  305.         Log " no driver has bound to interface 0 yet"
  306.         AddToList link_list $usb(idVendor):$usb(idProduct)
  307.  
  308.         # If device is known, the sh wrapper will take care, else:
  309.         if {[InBindList $usb(idVendor):$usb(idProduct)] == 0} {
  310.             Log "Device is not in \"bind_list\" yet, bind it now"
  311.  
  312.             # Load driver
  313.             CheckDriverBind $usb(idVendor) $usb(idProduct)
  314.  
  315.             # Old/slow systems may take a while to create the devices
  316.             set counter 0
  317.             while {![file exists $devdir/$ifdir/driver]} {
  318.                 if {$counter == 14} {break}
  319.                 after 500
  320.                 incr counter
  321.             }
  322.             if {$counter == 14} {
  323.                 Log " driver binding failed"
  324.             } else {
  325.                 Log " driver was bound to the device"
  326.                 AddToList bind_list $usb(idVendor):$usb(idProduct)
  327.             }
  328.         }
  329.     } else {
  330.         Log " driver has bound, device is known"
  331.         if {[llength [glob -nocomplain $devdir/$ifdir/ttyUSB*]] > 0} {
  332.             AddToList link_list $usb(idVendor):$usb(idProduct)
  333.         }
  334.     }
  335. } else {
  336.     # Just in case "NoDriverLoading" was added after the first bind
  337.     RemoveFromBindList $usb(idVendor):$usb(idProduct)
  338. }
  339.  
  340. if [regexp {ok:$} $report] {
  341.     # "NoDriverLoading" was set
  342.     Log "Doing no driver checking or binding for this device"
  343. }
  344.  
  345. # In newer kernels there is a switch to avoid the use of a device
  346. # reset (e.g. from usb-storage) which would possibly switch back
  347. # a mode-switching device to initial mode
  348. if [regexp {ok:} $report] {
  349.     Log "Checking for AVOID_RESET_QUIRK kernel attribute"
  350.     if [file exists $devdir/avoid_reset_quirk] {
  351.         if [catch {exec echo "1" >$devdir/avoid_reset_quirk 2>/dev/null} err] {
  352.             Log " Error setting the attribute: $err"
  353.         } else {
  354.             Log " AVOID_RESET_QUIRK activated"
  355.         }
  356.     } else {
  357.         Log " not present in this kernel"
  358.     }
  359. }
  360.  
  361. Log "\nAll done, exiting\n"
  362. SafeExit
  363.  
  364. }
  365. # end of proc {Main}
  366.  
  367.  
  368. proc {ReadSCSIAttrs} {topdir} {
  369.  
  370. global scsi
  371. set counter 0
  372. set sysdir $topdir
  373. Log "Checking storage tree in sysfs ..."
  374. while {$counter < 20} {
  375.     Log " loop $counter/20"
  376.     if {![file isdirectory $sysdir]} {
  377.         # Device is gone. Unplugged? Switched by kernel?
  378.         Log " sysfs device tree is gone; abort SCSI value check"
  379.         return 0
  380.     }
  381.     # Searching the storage/SCSI tree; might take a while
  382.     if {[set dirList [glob -nocomplain $topdir/host*]] != ""} {
  383.         set sysdir [lindex $dirList 0]
  384.         if {[set dirList [glob -nocomplain $sysdir/target*]] != ""} {
  385.             set sysdir [lindex $dirList 0]
  386.             regexp {.*target(.*)} $sysdir d subdir
  387.             if {[set dirList [glob -nocomplain $sysdir/$subdir*]] != ""} {
  388.                 set sysdir [lindex $dirList 0]
  389.                 if [file exists $sysdir/vendor] {
  390.                     Log " Storage tree is ready"
  391.                     break
  392.                 }
  393.             }
  394.         }
  395.     }
  396.     after 500
  397.     incr counter
  398. }
  399. if {$counter == 20} {
  400.     Log "SCSI tree not found; you may want to check if this path/file exists:"
  401.     Log "$sysdir/vendor\n"
  402.     return 0
  403. }
  404.  
  405. Log "Reading SCSI values ..."
  406. foreach attr {vendor model rev} {
  407.     if [file exists $sysdir/$attr] {
  408.         set rc [open $sysdir/$attr r]
  409.         set scsi($attr) [read -nonewline $rc]
  410.         close $rc
  411.     } else {
  412.         set scsi($attr) ""
  413.         Log "Warning: SCSI attribute \"$attr\" not found."
  414.     }
  415. }
  416. return 1
  417.  
  418. }
  419. # end of proc {ReadSCSIAttrs}
  420.  
  421.  
  422. proc {ReadUSBAttrs} {dir args} {
  423.  
  424. global usb
  425.  
  426. set attrList {idVendor idProduct bConfigurationValue manufacturer product serial devnum busnum bNumConfigurations}
  427. set mandatoryList {idVendor idProduct bNumConfigurations}
  428. set result 1
  429. if {$args != ""} {
  430.     lappend attrList "$args/bInterfaceClass"
  431.     lappend mandatoryList "$args/bInterfaceClass"
  432. }
  433. foreach attr $attrList {
  434.     if [file exists $dir/$attr] {
  435.         set rc [open $dir/$attr r]
  436.         set usb($attr) [string trim [read -nonewline $rc]]
  437.         close $rc
  438.     } else {
  439.         set usb($attr) ""
  440.         if {[lsearch $mandatoryList $attr] > -1} {
  441.             set result 0
  442.         }
  443.         if {$attr == "serial"} {continue}
  444.         Log "   Warning: USB attribute \"$attr\" not found"
  445.     }
  446. }
  447. return $result
  448.  
  449. }
  450. # end of proc {ReadUSBAttrs}
  451.  
  452.  
  453. proc {MatchDevice} {config} {
  454.  
  455. global scsi usb match
  456.  
  457. set devinfo [file tail $config]
  458. set infoList [split $devinfo :]
  459. set stringList [lrange $infoList 2 end]
  460. if {[llength $stringList] == 0} {return 1}
  461.  
  462. foreach teststring $stringList {
  463.     if {$teststring == "?"} {return 0}
  464.     set tokenList [split $teststring =]
  465.     set id [lindex $tokenList 0]
  466.     set matchstring [lindex $tokenList 1]
  467.     set blankstring ""
  468.     regsub -all {_} $matchstring { } blankstring
  469.     Log "matching $match($id)"
  470.     Log "  match string1 (exact):  $matchstring"
  471.     Log "  match string2 (blanks): $blankstring"
  472.     Log " device string: [set $match($id)]"
  473.     if {!([string match *$matchstring* [set $match($id)]] || [string match *$blankstring* [set $match($id)]])} {
  474.         return 0
  475.     }
  476. }
  477. return 1
  478.  
  479. }
  480. # end of proc {MatchDevice}
  481.  
  482.  
  483. proc {ParseGlobalConfig} {} {
  484.  
  485. global flags
  486. set configFile ""
  487. set places [list /etc/usb_modeswitch.conf /etc/sysconfig/usb_modeswitch /etc/default/usb_modeswitch]
  488. foreach cfg $places {
  489.     if [file exists $cfg] {
  490.         set configFile $cfg
  491.         break
  492.     }
  493. }
  494. if {$configFile == ""} {return}
  495.  
  496. set rc [open $configFile r]
  497. while {![eof $rc]} {
  498.     gets $rc line
  499.     if [regexp {DisableSwitching\s*=\s*([^\s]+)} $line d val] {
  500.         if [regexp -nocase {1|yes|true} $val] {
  501.             set flags(noswitching) 1
  502.         }
  503.     }
  504.     if [regexp {EnableLogging\s*=\s*([^\s]+)} $line d val] {
  505.         if [regexp -nocase {1|yes|true} $val] {
  506.             set flags(logging) 1
  507.         }
  508.     }
  509.  
  510. }
  511. return "Using global config file: $configFile"
  512.  
  513. }
  514. # end of proc {ParseGlobalConfig}
  515.  
  516.  
  517. proc ParseDeviceConfig {configContent} {
  518.  
  519. global config
  520. set config(driverModule) ""
  521. set config(driverIDPath) ""
  522. set config(waitBefore) ""
  523. set config(targetVendor) ""
  524. set config(targetProduct) ""
  525. set config(targetClass) ""
  526. set config(Configuration) ""
  527. set config(checkSuccess) 20
  528. set loadDriver 1
  529.  
  530. if [regexp -line {^[^#]*?TargetVendor.*?=.*?0x(\w+).*?$} $configContent d config(targetVendor)] {
  531.     Log "config: TargetVendor set to $config(targetVendor)"
  532. }
  533. if [regexp -line {^[^#]*?TargetProduct.*?=.*?0x(\w+).*?$} $configContent d config(targetProduct)] {
  534.     Log "config: TargetProduct set to $config(targetProduct)"
  535. }
  536. if [regexp -line {^[^#]*?TargetProductList.*?=.*?"([0-9a-fA-F,]+).*?$} $configContent d config(targetProduct)] {
  537.     Log "config: TargetProductList set to $config(targetProduct)"
  538. }
  539. if [regexp -line {^[^#]*?TargetClass.*?=.*?0x(\w+).*?$} $configContent d config(targetClass)] {
  540.     Log "config: TargetClass set to $config(targetClass)"
  541. }
  542. if [regexp -line {^[^#]*?Configuration.*?=.*?([0-9]+).*?$} $configContent d config(Configuration)] {
  543.     Log "config: Configuration (target) set to $config(Configuration)"
  544. }
  545. if [regexp -line {^[^#]*?DriverModule.*?=.*?(\w+).*?$} $configContent d config(driverModule)] {
  546.     Log "config: DriverModule set to $config(driverModule)"
  547. }
  548. if [regexp -line {^[^#]*?DriverIDPath.*?=.*?"?([/\-\w]+).*?$} $configContent d config(driverIDPath)] {
  549.     Log "config: DriverIDPath set to $config(driverIDPath)"
  550. }
  551. if [regexp -line {^[^#]*?CheckSuccess.*?=.*?([0-9]+).*?$} $configContent d config(checkSuccess)] {
  552.     Log "config: CheckSuccess set to $config(checkSuccess)"
  553. }
  554. if [regexp -line {^[^#]*?WaitBefore.*?=.*?([0-9]+).*?$} $configContent d config(waitBefore)] {
  555.     Log "config: WaitBefore set to $config(waitBefore)"
  556. }
  557. if [regexp -line {^[^#]*?NoDriverLoading.*?=.*?(1|yes|true).*?$} $configContent] {
  558.     set loadDriver 0
  559.     Log "config: NoDriverLoading is set to active"
  560. }
  561.  
  562. # For general driver loading; TODO: add respective device names.
  563. # Presently only useful for HSO devices (which are recounted now)
  564. if $loadDriver {
  565.     if {$config(driverModule) == ""} {
  566.         set config(driverModule) "option"
  567.         set config(driverIDPath) "/sys/bus/usb-serial/drivers/option1"
  568.     } else {
  569.         if {$config(driverIDPath) == ""} {
  570.             set config(driverIDPath) "/sys/bus/usb/drivers/$config(driverModule)"
  571.         }
  572.     }
  573.     Log "Driver module is \"$config(driverModule)\", ID path is $config(driverIDPath)\n"
  574. } else {
  575.     Log "Driver will not be handled by usb_modeswitch"
  576. }
  577. set config(waitBefore) [string trimleft $config(waitBefore) 0]
  578.  
  579. }
  580. # end of proc {ParseDeviceConfig}
  581.  
  582.  
  583. proc ConfigGet {command config} {
  584.  
  585. global settings
  586.  
  587. switch $command {
  588.  
  589.     conflist {
  590.         # 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] {
  596.                 Log "Error: problem opening config package; tar returned\n $err"
  597.                 return {}
  598.             }
  599.             set packedList [split $packedList \n]
  600.             set packedConfigList [lsort -decreasing [lsearch -glob -all -inline $packedList $config*]]
  601.             # Now add packaged configs with a mark, again sorted for priority
  602.             foreach packedConfig $packedConfigList {
  603.                 lappend configList "pack/$packedConfig"
  604.             }
  605.         }
  606.  
  607.         return $configList
  608.     }
  609.     conffile {
  610.         if [regexp {^pack/} $config] {
  611.             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]
  614.         } else {
  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"
  618.                 SysLog "usb_modeswitch: please report any new or corrected settings; otherwise, check for outdated files"
  619.             }
  620.             set rc [open $config r]
  621.             set configContent [read $rc]
  622.             close $rc
  623.         }
  624.         return $configContent
  625.     }
  626. }
  627.  
  628. }
  629. # end of proc {ConfigGet}
  630.  
  631. proc {Log} {msg} {
  632.  
  633. global flags device
  634. if {$flags(logging) == 0} {return}
  635.  
  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"
  640.             return
  641.         } else {
  642.             puts $flags(wc) "Error opening log file ($err), redirect to console"
  643.         }
  644.     }
  645.     puts $flags(wc) "\n\nUSB_ModeSwitch log from [clock format [clock seconds]]\n"
  646. }
  647. if {$flags(wc) == "error"} {return}
  648. puts $flags(wc) $msg
  649.  
  650. }
  651. # end of proc {Log}
  652.  
  653.  
  654. # Closing the log file if open and exit
  655. proc {SafeExit} {} {
  656.  
  657. global flags
  658. if [info exists flags(wc)] {
  659.     catch {close $flags(wc)}
  660. }
  661. exit
  662.  
  663. }
  664. # end of proc {SafeExit}
  665.  
  666.  
  667. proc {SymLinkName} {path} {
  668. global device
  669.  
  670. proc {hasInterrupt} {ifDir} {
  671.     if {[llength [glob -nocomplain $ifDir/ttyUSB*]] == 0} {
  672.         Log "  no ttyUSB interface - skip checking endpoints"
  673.         return 0
  674.     }
  675.     foreach epDir [glob -nocomplain $ifDir/ep_*] {
  676.         set e [file tail $epDir]
  677.         Log "  checking $e ..."
  678.         if [file exists $epDir/type] {
  679.             set rc [open $epDir/type r]
  680.             set type [read $rc]
  681.             close $rc
  682.             if [regexp {Interrupt} $type] {
  683.                 Log "  $e has interrupt transfer type"
  684.                 return 1
  685.             }
  686.         }
  687.     }
  688.     return 0
  689. }
  690.  
  691. set loginit "usb_modeswitch called with --symlink-name\n parameter: $path\n"
  692.  
  693. # In case the device path is returned as /class/tty/ttyUSB,
  694. # get the USB device path from linked tree "device"
  695. set linkpath /sys$path/device
  696. if [file exists $linkpath] {
  697.     if {[file type $linkpath] == "link"} {
  698.         set rawpath [file readlink $linkpath]
  699.         set trimpath [regsub -all {\.\./} $rawpath {}]
  700.          if [file isdirectory /sys/$trimpath] {
  701.             append loginit "\n Using path $path\n"
  702.             set path /$trimpath
  703.         }
  704.     }
  705. }
  706.  
  707. if {![regexp {ttyUSB[0-9]+} $path myPort]} {
  708.     if $flags(logging) {
  709.         set device [clock clicks]
  710.         Log "$loginit\nThis is not a ttyUSB port. Aborting"
  711.     }
  712.     return ""
  713. }
  714.  
  715. set device $myPort
  716. Log "$loginit\nMy name is $myPort\n"
  717.  
  718. if {![regexp {(.*?[0-9]+)\.([0-9]+)/ttyUSB} /sys$path d ifRoot ifNum]} {
  719.     Log "Could not find interface in path\n $path. Aborting"
  720.     return ""
  721. }
  722.  
  723. set ifDir $ifRoot.$ifNum
  724.  
  725. Log "Checking my endpoints ...\n in $ifDir"
  726. if [hasInterrupt $ifDir] {
  727.     Log "\n--> I am an interrupt port"
  728.     set rightPort 1
  729. } else {
  730.     Log "\n--> I am not an interrupt port\n"
  731.     set rightPort 0
  732. }
  733.  
  734. # There are devices with more than one interrupt interface.
  735. # Assume that the lowest of these is usable. Check all
  736. # possible lower interfaces
  737.  
  738. if { $rightPort && ($ifNum > 0) } {
  739.     Log "\nLooking for lower ports with interrupt endpoints"
  740.     for {set i 0} {$i < $ifNum} {incr i} {
  741.         set ifDir $ifRoot.$i
  742.         Log " in ifDir $ifDir ..."
  743.         if [hasInterrupt $ifDir] {
  744.             Log "\n--> found an interrupt interface below me\n"
  745.             set rightPort 0
  746.             break
  747.         }
  748.     }
  749. }
  750. if {$rightPort == 0} {
  751.     Log "Return empty name and exit"
  752.     return ""
  753. }
  754.  
  755. Log "\n--> No interrupt interface below me\n"
  756.  
  757. cd /dev
  758. set idx 2
  759. set symlinkName "gsmmodem"
  760. while {$idx < 256} {
  761.     if {![file exists $symlinkName]} {
  762.         set placeholder [open /dev/$symlinkName w]
  763.         close $placeholder
  764.         break
  765.     }
  766.     set symlinkName gsmmodem$idx
  767.     incr idx
  768. }
  769. if {$idx == 256} {return ""}
  770.  
  771. Log "Return symlink name \"$symlinkName\" and exit"
  772. return $symlinkName
  773.  
  774. }
  775. # end of proc {SymLinkName}
  776.  
  777.  
  778. # Load and bind driver (default "option")
  779. #
  780. proc {CheckDriverBind} {vid pid} {
  781. global config
  782.  
  783. foreach fn {/sbin/modprobe /usr/sbin/modprobe} {
  784.     if [file exists $fn] {
  785.         set loader $fn
  786.     }
  787. }
  788. Log "Module loader is $loader"
  789.  
  790. set idfile $config(driverIDPath)/new_id
  791. if {![file exists $idfile]} {
  792.     if {$loader == ""} {
  793.         Log "Can't do anymore without module loader; get \"modtools\"!"
  794.         return
  795.     }
  796.     Log "\nTrying to load module \"$config(driverModule)\""
  797.     if [catch {set result [exec $loader -v $config(driverModule)]} err] {
  798.         Log " Running \"$loader $config(driverModule)\" gave an error:\n  $err"
  799.     } else {
  800.         Log " Module was loaded successfully:\n$result"
  801.     }
  802. } else {
  803.     Log "Module is active already"
  804. }
  805. set i 0
  806. while {$i < 50} {
  807.     if [file exists $idfile] {
  808.         break
  809.     }
  810.     after 20
  811.     incr i
  812. }
  813. if {$i < 50} {
  814.     Log "Trying to add ID to driver \"$config(driverModule)\""
  815.     SysLog "usb_modeswitch: adding device ID $vid:$pid to driver \"$config(driverModule)\""
  816.     SysLog "usb_modeswitch: please report the device ID to the Linux USB developers!"
  817.     if [catch {exec echo "$vid $pid ff" >$idfile} err] {
  818.         Log " Error adding ID to driver:\n  $err"
  819.     } else {
  820.         Log " ID added to driver; check for new devices in /dev"
  821.     }
  822. } else {
  823.     Log " \"$idfile\" not found, check if kernel version is at least 2.6.27"
  824.     Log "Falling back to \"usbserial\""
  825.     set config(driverModule) usbserial
  826.     Log "\nTrying to unload driver \"usbserial\""
  827.     if [catch {exec $loader -r usbserial} err] {
  828.         Log " Running \"$loader -r usbserial\" gave an error:\n  $err"
  829.         Log "No more fallbacks"
  830.         return
  831.     }
  832.     after 50
  833.     Log "\nTrying to load driver \"usbserial\" with device IDs"
  834.     if [catch {set result [exec $loader -v usbserial vendor=0x$vid product=0x$pid]} err] {
  835.         Log " Running \"$loader usbserial\" gave an error:\n  $err"
  836.     } else {
  837.         Log " Driver was loaded successfully:\n$result"
  838.     }
  839. }
  840.  
  841. }
  842. # end of proc {CheckDriverBind}
  843.  
  844.  
  845. # Check if USB ID is listed as needing driver binding
  846. proc {InBindList} {id} {
  847.  
  848. set listfile /var/lib/usb_modeswitch/bind_list
  849. if {![file exists $listfile]} {return 0}
  850. set rc [open $listfile r]
  851. set buffer [read $rc]
  852. close $rc
  853. if [string match *$id* $buffer] {
  854. Log "Found $id in bind_list"
  855.     return 1
  856. } else {
  857. Log "No $id in bind_list"
  858.     return 0
  859. }
  860.  
  861. }
  862. # end of proc {InBindList}
  863.  
  864. # Add USB ID to list of devices needing later treatment
  865. proc {AddToList} {name id} {
  866.  
  867. set listfile /var/lib/usb_modeswitch/$name
  868. set oldlistfile /etc/usb_modeswitch.d/bind_list
  869.  
  870. if {($name == "bind_list") && [file exists $oldlistfile] && ![file exists $listfile]} {
  871.     if [catch {file rename $oldlistfile $listfile} err] {
  872.         Log "Error renaming the old bind list file ($err)"
  873.         return
  874.     }
  875. }
  876.  
  877. if [file exists $listfile] {
  878.     set rc [open $listfile r]
  879.     set buffer [read $rc]
  880.     close $rc
  881.     if [string match *$id* $buffer] {
  882.         return
  883.     }
  884.     set idList [split [string trim $buffer] \n]
  885. }
  886. lappend idList $id
  887. set buffer [join $idList "\n"]
  888. if [catch {set lc [open $listfile w]}] {return}
  889. puts $lc $buffer
  890. close $lc
  891.  
  892. }
  893. # end of proc {AddToList}
  894.  
  895.  
  896. # Remove USB ID from bind list (NoDriverLoading is set)
  897. proc {RemoveFromBindList} {id} {
  898.  
  899. set listfile /var/lib/usb_modeswitch/bind_list
  900. if [file exists $listfile] {
  901.     set rc [open $listfile r]
  902.     set buffer [read $rc]
  903.     close $rc
  904.     set idList [split [string trim $buffer] \n]
  905. } else {
  906.     return
  907. }
  908. set idx [lsearch $idList $id]
  909. if {$idx > -1} {
  910.     set idList [lreplace $idList $idx $idx]
  911. } else {
  912.     return
  913. }
  914. if {[llength $idList] == 0} {
  915.     file delete $listfile
  916.     return
  917. }
  918. set buffer [join $idList "\n"]
  919. if [catch {set lc [open $listfile w]}] {return}
  920. puts $lc $buffer
  921. close $lc
  922.  
  923. }
  924. # end of proc {RemoveFromBindList}
  925.  
  926. proc {CheckSuccess} {devdir} {
  927.  
  928. global config usb
  929. set ifdir "[file tail $devdir]:1.0"
  930.  
  931. if {[string length $config(targetClass)] || [string length $config(Configuration)]} {
  932.     set config(targetVendor) $usb(idVendor)
  933.     set config(targetProduct) $usb(idProduct)
  934. }
  935. Log "Checking success of mode switch for max. $config(checkSuccess) seconds ..."
  936.  
  937. for {set i 1} {$i <= $config(checkSuccess)} {incr i} {
  938.     after 1000
  939.     if {![file isdirectory $devdir]} {
  940.         Log " Waiting for device file system ($i sec.) ..."
  941.         continue
  942.     } else {
  943.         Log " Reading attributes ..."
  944.     }
  945.     if {![ReadUSBAttrs $devdir $ifdir]} {
  946.         Log " Essential attributes are missing, continue wait ..."
  947.         continue
  948.     }
  949.     if [string length $config(targetClass)] {
  950.         if {![regexp $usb($ifdir/bInterfaceClass) $config(targetClass)]} {continue}
  951.     }
  952.     if [string length $config(Configuration)] {
  953.         if {$usb(bConfigurationValue) != $config(Configuration} {continue}
  954.     }
  955.     if {![regexp $usb(idVendor) $config(targetVendor)]} {continue}
  956.     if {![regexp $usb(idProduct) $config(targetProduct)]} {continue}
  957.     Log " All attributes matched"
  958.     break
  959. }
  960. if {$i > 20} {
  961.     return 0
  962. }
  963. return 1
  964.  
  965. }
  966. # end of proc {CheckSuccess}
  967.  
  968. proc {SysLog} {msg} {
  969.  
  970. global flags
  971. if {![info exists flags(logger)]} {
  972.     set flags(logger) ""
  973.     foreach fn {/bin/logger /usr/bin/logger} {
  974.         if [file exists $fn] {
  975.             set flags(logger) $fn
  976.         }
  977.     }
  978.     Log "Logger is $flags(logger)"
  979. }
  980. if {$flags(logger) == ""} {
  981.     Log "Can't add system message, no syslog helper found"
  982.     return
  983. }
  984. catch {exec $flags(logger) -p syslog.notice "$msg" 2>/dev/null}
  985.  
  986. }
  987. # end of proc {SysLog}
  988.  
  989.  
  990.  
  991. # The actual entry point
  992. Main $argv $argc
  993.