~ubuntu-branches/ubuntu/utopic/usb-modeswitch/utopic-proposed

« back to all changes in this revision

Viewing changes to usb_modeswitch.sh

  • Committer: Bazaar Package Importer
  • Author(s): Didier Raboud
  • Date: 2010-05-03 11:37:08 UTC
  • mfrom: (1.2.1 upstream) (8.1.1 sid)
  • Revision ID: james.westby@ubuntu.com-20100503113708-y9s2bbgvr264bmmb
Tags: 1.1.2-2
Add 03_filter_undesired_rules.patch to filter out undesired files
(Closes: #579981)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/bin/sh
2
 
# next lines for bash, ignored by tclsh, restarting in background\
3
 
export PATH=/bin:/usr/bin; \
4
 
if [ ! -e "/usr/bin/tclsh" ]; then \
5
 
        logger -p syslog.error "usb_modeswitch: tcl shell not found, install tcl package!"; \
6
 
fi; \
7
 
/usr/bin/tclsh "$0" "$@" &>/dev/null & \
8
 
sleep 1; \
9
 
exit
10
 
 
11
 
 
12
 
# Wrapper (tcl) for usb_modeswitch, called by
13
 
# /lib/udev/rules.d/40-usb_modeswitch.rules
14
 
#
15
 
# Does ID check on hotplugged USB devices and calls the
16
 
# mode switching program with the matching parameter file
17
 
# from /etc/usb_modeswitch.d
18
 
#
19
 
# Part of usb-modeswitch-1.1.0 package
20
 
# (C) Josua Dietze 2009, 2010
21
 
 
22
 
 
23
 
# Change this to 1 if you want verbose logging
24
 
# to /var/log/usb_modeswitch_<device-interface>
25
 
 
26
 
set logging 0
27
 
 
28
 
 
29
 
set env(PATH) "/bin:/usr/bin"
30
 
 
31
 
# Execution starts at file bottom
32
 
 
33
 
proc {Main} {argc argv} {
34
 
 
35
 
global scsi usb match wc logging device
36
 
 
37
 
set dbdir       /etc/usb_modeswitch.d
38
 
set bindir      /usr/sbin
39
 
 
40
 
# argv contains the values provided from the udev rule
41
 
# separated by "/"
42
 
 
43
 
set argList [split [lindex $argv 0] /]
44
 
 
45
 
if [string length [lindex $argList 1]] {
46
 
        set device [lindex $argList 1]
47
 
} else {
48
 
        set device "noname"
49
 
}
50
 
 
51
 
Log "raw args from udev: $argv"
52
 
 
53
 
if {$device == "noname"} {
54
 
        Log "No data from udev. Exiting"
55
 
        SafeExit
56
 
}
57
 
 
58
 
if {![string match *0 $device]} {
59
 
        Log "Interface is not 0. Not the install storage. Exiting"
60
 
        SafeExit
61
 
}
62
 
 
63
 
 
64
 
# arg 0: the bus id for the device (udev: %b)
65
 
# arg 1: the "kernel name" for the device (udev: %k)
66
 
#
67
 
# Both together give the top directory where the path
68
 
# to the SCSI attributes can be determined (further down)
69
 
# Addendum: older kernel/udev version seem to differ in
70
 
# providing these attributes - or not. So more probing
71
 
# is needed
72
 
 
73
 
if {[string length [lindex $argList 0]] == 0} {
74
 
        if {[string length [lindex $argList 1]] == 0} {
75
 
                Log "No device number values given from udev! Exiting"
76
 
                SafeExit
77
 
        } else {
78
 
                Log "Bus ID for device not given by udev."
79
 
                Log " Trying to determine it from kernel name ([lindex $argList 1]) ..."
80
 
                if {![regexp {(.*?):} [lindex $argList 1] d dev_top]} {
81
 
                        Log "Could not determine top device dir from udev values! Exiting"
82
 
                        SafeExit
83
 
                }
84
 
        }
85
 
} else {
86
 
        set dev_top [lindex $argList 0]
87
 
        regexp {(.*?):} $dev_top d dev_top
88
 
}
89
 
 
90
 
 
91
 
set devdir /sys/bus/usb/devices/$dev_top
92
 
if {![file isdirectory $devdir]} {
93
 
        Log "Top sysfs directory not found ($devdir)! Exiting"
94
 
        SafeExit
95
 
}
96
 
 
97
 
 
98
 
# Mapping of the short string identifiers (in the config
99
 
# file names) to the long name used here
100
 
#
101
 
# If we need them it's a snap to add new attributes here!
102
 
 
103
 
set match(sVe) scsi(vendor)
104
 
set match(sMo) scsi(model)
105
 
set match(sRe) scsi(rev)
106
 
set match(uMa) usb(manufacturer)
107
 
set match(uPr) usb(product)
108
 
set match(uSe) usb(serial)
109
 
 
110
 
 
111
 
# Now reading the USB attributes
112
 
 
113
 
ReadUSBAttrs $devdir
114
 
 
115
 
if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
116
 
        Log "USB IDs not found in sysfs tree. Exiting"
117
 
        SafeExit
118
 
}
119
 
 
120
 
Log "----------------\nUSB values from sysfs:"
121
 
foreach attr {manufacturer product serial} {
122
 
        Log "  $attr\t$usb($attr)"
123
 
}
124
 
Log "----------------"
125
 
 
126
 
# Check if there is more than one config file for this USB ID,
127
 
# which would point to a possible ambiguity. If so, check if
128
 
# SCSI values are needed
129
 
 
130
 
set configList [glob -nocomplain $dbdir/$usb(idVendor):$usb(idProduct)*]
131
 
if {[llength $configList] == 0} {
132
 
        Log "Aargh! Config file missing for $usb(idVendor):$usb(idProduct)! Exiting"
133
 
        SafeExit
134
 
}
135
 
 
136
 
set scsiNeeded false
137
 
if {[llength $configList] > 1} {
138
 
        if [regexp {:s} $configList] {
139
 
                set scsiNeeded true
140
 
        }
141
 
}
142
 
if {!$scsiNeeded} {
143
 
        Log "SCSI attributes not needed, moving on"
144
 
}
145
 
 
146
 
 
147
 
# Getting the SCSI values via libusb results in a detached
148
 
# usb-storage driver. Not good for devices that want to be
149
 
# left alone. Fortunately, the sysfs tree provides the values
150
 
# too without need for direct access
151
 
 
152
 
# First we wait until the SCSI data is ready - or timeout.
153
 
# Timeout means: no storage driver was bound to the device.
154
 
# We run 20 times max, every half second (max. 10 seconds
155
 
# total)
156
 
 
157
 
# We also check if the device itself changes, probably
158
 
# because it was switched by the kernel (or even unplugged).
159
 
# Then we do simply nothing and exit quietly ...
160
 
 
161
 
set counter 0
162
 
while {$scsiNeeded && $counter < 20} {
163
 
        after 500
164
 
        incr counter
165
 
        Log "waiting for storage tree in sysfs"
166
 
 
167
 
        set sysdir $devdir/[lindex $argList 1]
168
 
 
169
 
        if {![file isdirectory $sysdir]} {
170
 
                # Device is gone. Unplugged? Switched by kernel?
171
 
                Log "sysfs device tree is gone; exiting"
172
 
                SafeExit
173
 
        }
174
 
        set rc [open $devdir/product r]
175
 
        set newproduct [read -nonewline $rc]
176
 
        close $rc
177
 
        if {![string match $newproduct $usb(product)]} {
178
 
                # Device has just changed. Switched by someone else?
179
 
                Log "device has changed; exiting"
180
 
                SafeExit
181
 
        }
182
 
 
183
 
        # Searching the storage/SCSI tree; might take a while
184
 
        if {[set dirList [glob -nocomplain $sysdir/host*]] != ""} {
185
 
                set sysdir [lindex $dirList 0]
186
 
                if {[set dirList [glob -nocomplain $sysdir/target*]] != ""} {
187
 
                        set sysdir [lindex $dirList 0]
188
 
                        regexp {.*target(.*)} $sysdir d subdir
189
 
                        if {[set dirList [glob -nocomplain $sysdir/$subdir*]] != ""} {
190
 
                                set sysdir [lindex $dirList 0]
191
 
                                if [file exists $sysdir/vendor] {
192
 
                                        # Finally SCSI structure is ready, get the values
193
 
                                        ReadSCSIAttrs $sysdir
194
 
                                        Log "SCSI values read"
195
 
                                        break
196
 
                                }
197
 
                        }
198
 
                }
199
 
        }
200
 
}
201
 
if $scsiNeeded {
202
 
        if {$counter == 20 && [string length $scsi(vendor)] == 0} {
203
 
                Log "SCSI tree not found; you may want to check if this path/file exists:"
204
 
                Log "$sysdir/vendor\n"
205
 
        } else {
206
 
                Log "----------------\nSCSI values from sysfs:"
207
 
                foreach attr {vendor model rev} {
208
 
                        Log " $attr\t$scsi($attr)"
209
 
                }
210
 
                Log "----------------"
211
 
        }
212
 
        Log "Waiting 3 secs. after SCSI device was added"
213
 
        after 3000
214
 
} else {
215
 
        after 500
216
 
}
217
 
 
218
 
# If SCSI tree in sysfs was not identified, try and get the values
219
 
# from a (nonswitching) call of usb_modeswitch; this detaches the
220
 
# storage driver, so it's just the last resort
221
 
 
222
 
if {$scsiNeeded && $scsi(vendor)==""} {
223
 
        set testSCSI [exec $bindir/usb_modeswitch -v 0x$usb(idVendor) -p 0x$usb(idProduct)]
224
 
        regexp {  Vendor String: (.*?)\n} $testSCSI d scsi(vendor)
225
 
        regexp {   Model String: (.*?)\n} $testSCSI d scsi(model)
226
 
        regexp {Revision String: (.*?)\n} $testSCSI d scsi(rev)
227
 
        Log "SCSI values from usb_modeswitch:"
228
 
        foreach attr {vendor model rev} {
229
 
                Log " $attr\t$scsi($attr)"
230
 
        }
231
 
}
232
 
 
233
 
# If we don't have the SCSI values by now, we just
234
 
# leave the variables empty; they won't match anything
235
 
 
236
 
# Time to check for a matching config file.
237
 
# Matching itself is done by MatchDevice
238
 
#
239
 
# Sorting the configuration file names reverse so that
240
 
# the ones with matching additions are tried first; the
241
 
# common configs without matching are used at the end and
242
 
# provide a kind of fallback
243
 
 
244
 
set report {}
245
 
set configList [glob -nocomplain $dbdir/$usb(idVendor):$usb(idProduct)*]
246
 
foreach configuration [lsort -decreasing $configList] {
247
 
        Log "checking config: $configuration"
248
 
        if [MatchDevice $configuration] {
249
 
                set switch_config $configuration
250
 
                set devList1 [glob -nocomplain /dev/ttyUSB* /dev/ttyACM* /dev/ttyHS*]
251
 
                Log "! matched, now switching"
252
 
                if $logging {
253
 
                        Log " (running command: $bindir/usb_modeswitch -I -W -c $configuration)"
254
 
                        set report [exec $bindir/usb_modeswitch -I -W -D -c $configuration 2>@ stdout]
255
 
                } else {
256
 
                        set report [exec $bindir/usb_modeswitch -I -Q -D -c $configuration]
257
 
                }
258
 
                Log "\nverbose output of usb_modeswitch:"
259
 
                Log "--------------------------------"
260
 
                Log $report
261
 
                Log "--------------------------------"
262
 
                Log "(end of usb_modeswitch output)\n"
263
 
                break
264
 
        } else {
265
 
                Log "* no match, not switching with this config"
266
 
        }
267
 
}
268
 
 
269
 
# We're finished with switching; success checking
270
 
# was done by usb_modeswitch and logged via syslog.
271
 
#
272
 
# If switching was OK we now check for drivers by
273
 
# simply recounting serial devices under /dev
274
 
 
275
 
if [regexp {ok:} $report] {
276
 
        # some settle time in ms
277
 
        after 500
278
 
 
279
 
        Log "Now checking for newly created serial devices ..."
280
 
        set devList2 [glob -nocomplain /dev/ttyUSB* /dev/ttyACM* /dev/ttyHS*]
281
 
 
282
 
        if {[llength $devList1] >= [llength $devList2]} {
283
 
                Log " no new serial devices found"
284
 
                
285
 
                # Backup for unknown target IDs: check sysfs again
286
 
                # as soon as device is back
287
 
                if [regexp {ok:0000:0000} $report] {
288
 
                        for {set i 0} {$i < 19} {incr i} {
289
 
                                if {![file exists $devdir/idProduct]} {
290
 
                                        after 1000
291
 
                                        continue
292
 
                                }
293
 
                        }
294
 
                }
295
 
                ReadUSBAttrs $devdir
296
 
                if {[string length "$usb(idVendor)$usb(idProduct)"] < 8} {
297
 
                        regexp {ok:(\w{4}):(\w{4})} $report d usb(idVendor) usb(idProduct)
298
 
                }
299
 
                set t "$usb(idVendor)$usb(idProduct)"
300
 
                if {[string length $t] == 8 && [string trim $t 0] != ""} {
301
 
                        set idfile /sys/bus/usb-serial/drivers/option1/new_id
302
 
                        if {![file exists $idfile]} {
303
 
                                Log "\nTrying to load the option driver"
304
 
                                set loader /sbin/modprobe
305
 
                                Log " loader is: $loader"
306
 
                                if [file exists $loader] {
307
 
                                        set result [exec $loader -v option]
308
 
                                        if {[regexp {not found} $result]} {
309
 
                                                Log " option driver not present as module"
310
 
                                        }
311
 
                                } else {
312
 
                                        Log " /sbin/modprobe not found"
313
 
                                }
314
 
                        }
315
 
                        if [file exists $idfile] {
316
 
                                Log "Trying to add ID to option driver"
317
 
                                catch {exec logger -p syslog.notice "usb_modeswitch: adding device ID $usb(idVendor):$usb(idProduct)" to driver \"option\""}
318
 
                                #"
319
 
                                exec echo "$usb(idVendor) $usb(idProduct)" >$idfile
320
 
                                after 600
321
 
                                set devList2 [glob -nocomplain /dev/ttyUSB* /dev/ttyACM*]
322
 
                                if {[llength $devList1] >= [llength $devList2]} {
323
 
                                        Log " still no new serial devices found"
324
 
                                } else {
325
 
                                        Log " driver successfully bound"
326
 
                                }
327
 
                        }
328
 
                }
329
 
        } else {
330
 
                Log " new serial devices found, driver has bound"
331
 
        }
332
 
}
333
 
 
334
 
Log "\nAll done, exiting\n"
335
 
SafeExit
336
 
 
337
 
}
338
 
# end of proc {Main}
339
 
 
340
 
 
341
 
proc {ReadSCSIAttrs} {dir} {
342
 
 
343
 
global scsi
344
 
Log "SCSI dir exists: $dir"
345
 
 
346
 
foreach attr {vendor model rev} {
347
 
        if [file exists $dir/$attr] {
348
 
                set rc [open $dir/$attr r]
349
 
                set scsi($attr) [read -nonewline $rc]
350
 
                close $rc
351
 
        } else {
352
 
                set scsi($attr) ""
353
 
                Log "Warning: SCSI attribute \"$attr\" not found."
354
 
        }
355
 
}
356
 
 
357
 
}
358
 
# end of proc {ReadSCSIAttrs}
359
 
 
360
 
 
361
 
proc {ReadUSBAttrs} {dir} {
362
 
 
363
 
global usb
364
 
Log "USB dir exists: $dir"
365
 
 
366
 
foreach attr {idVendor idProduct manufacturer product serial} {
367
 
        if [file exists $dir/$attr] {
368
 
                set rc [open $dir/$attr r]
369
 
                set usb($attr) [read -nonewline $rc]
370
 
                close $rc
371
 
        } else {
372
 
                set usb($attr) ""
373
 
                Log "Warning: USB attribute \"$attr\" not found."
374
 
        }
375
 
}
376
 
 
377
 
}
378
 
# end of proc {ReadUSBAttrs}
379
 
 
380
 
 
381
 
proc {MatchDevice} {config} {
382
 
 
383
 
global scsi usb match
384
 
 
385
 
set devinfo [file tail $config]
386
 
set infoList [split $devinfo :]
387
 
set stringList [lrange $infoList 2 end]
388
 
if {[llength $stringList] == 0} {return 1}
389
 
 
390
 
foreach teststring $stringList {
391
 
        if {$teststring == "?"} {return 0}
392
 
        set tokenList [split $teststring =]
393
 
        set id [lindex $tokenList 0]
394
 
        set matchstring [lindex $tokenList 1]
395
 
        regsub -all {_} $matchstring { } matchstring
396
 
        Log "matching $match($id)"
397
 
        Log "  match string: $matchstring"
398
 
        Log " device string: [set $match($id)]"
399
 
        if {![string match $matchstring* [set $match($id)]] } {
400
 
                return 0
401
 
        }
402
 
}
403
 
return 1
404
 
 
405
 
}
406
 
# end of proc {MatchDevice}
407
 
 
408
 
 
409
 
proc {Log} {msg} {
410
 
 
411
 
global wc logging device
412
 
if {$logging == 0} {return}
413
 
if {![info exists wc]} {
414
 
        set wc [open /var/log/usb_modeswitch_$device a+]
415
 
        puts $wc "\n\nUSB_ModeSwitch log from [clock format [clock seconds]]\n"
416
 
} else {
417
 
#       set wc [open /var/log/usb_modeswitch_$device a+]
418
 
}
419
 
puts $wc $msg
420
 
#close $wc
421
 
 
422
 
}
423
 
# end of proc {Log}
424
 
 
425
 
 
426
 
proc {SafeExit} {} {
427
 
global wc
428
 
if [info exists wc] {
429
 
        catch {close $wc}
430
 
}
431
 
exit
432
 
 
433
 
}
434
 
# end of proc {SafeExit}
435
 
 
436
 
 
437
 
# The actual entry point
438
 
Main $argc $argv