6 proc debug {m} { puts "DEBUG $m" }
8 proc log {m} { puts "LOG $m" }
14 proc find-devices {} {
15 global errorCode errorInfo devices expected_devs
16 set base /sys/class/tty
17 foreach candidate [glob -nocomplain -directory $base -tails ttyACM*] {
18 debug "candidate $candidate"
19 if {[catch { file link $base/$candidate/device } ltarget]} {
20 debug " readlink failed [lrange $errorCode 0 1]"
21 switch -glob $errorCode {
22 {POSIX EINVAL *} continue
23 {POSIX ENOENT *} continue
24 default { error "$ltarget \[$errorCode] $errorInfo $errorCode" }
27 if {![regexp {^(.*)\.(\d+)$} $ltarget dummy dbase interf]} {
28 debug " readlink bad target $ltarget"
31 debug " approved $dbase $interf $candidate"
32 lappend devs($dbase) [list $interf $candidate]
34 set howmany [array size devs]
36 experror "no appropriate device(s) found"
39 experror "several appropriate device(s) found [array names $devs]"
42 foreach dev [lsort -index 0 -integer $devs([lindex [array names devs] 0])] {
43 lappend devices [lindex $dev 1]
45 if {[llength $devices] != $expected_devs} {
46 experror "wrong # devices ($devices), expected $expected"
50 proc create-dev-nodes {} {
51 global devices expected_devs
52 set ourdevs /dev/atmux
54 for {set i 0} {$i < $expected_devs-1} {incr i} {
56 file link -symbolic ../$device $ourdevs/.new.$new
57 file rename -force $ourdevs/.new.$new $ourdevs/$new
60 foreach candidate [glob -nocomplain -directory $ourdevs -tails ttyAT*] {
61 if {![info exists wanted($candidate)]} {
62 file remove $ourdevs/$candidate
67 proc reopen-our-device {} {
69 set dchan [open /dev/[lindex $devices 1] r+]
70 fconfigure $dchan -blocking no -buffering line -translation {crlf cr}
71 read $dchan; # flush input
75 set result [read $dchan]
76 if {![regexp -line {^OK$} $result]} { experror "got [logquote $result]" }
77 fileevent $dchan readable chan-readable $dchan dchan "modem device"
86 proc dchan-failure {dummy} {
88 if {[info exists dchan]} {
89 catch { close $dchan }
94 proc try-open-our-device {} {
102 sendout-async *TTYATMUX "*TTYATMUXDEVS [join $devices ,]"
103 sendout-async *TTYATMUX "*TTYATMUXOPEN"
106 proc trap-log {what body var} {
107 global errorCode errorInfo
114 switch -glob $errorCode {
115 {POSIX *} - EXPECTED { log "$what: $result" }
117 log "unexpected: $what: $result"
118 foreach l [split $errorInfo "\n"] { log " $l" }
127 return -code $rc -errorinfo $errorInfo \
128 -errorcode $errorCode $result
133 proc chan-readable {chan how what args} {
135 if {[trap-log "$what failure" {
142 if {![eof $chan]} return
147 trap-log "error processing $what data" {
153 proc async-notif fixme this has wrong arguments
155 proc async-notif-or-resp-fixed {asid l} {
156 global current_command_asid
157 if {![string compare $asid $current_command_asid]} {
164 proc async-notif-creg {asid l} {
165 set ll [llength [split $l ,]]
169 async-notif {+CREG} 2 2 $l
170 async-notif {+CREG} 1 1 [lindex [split $l ,] 0]
174 async-notif {+CREG} 1 2 $l
177 bad-data $l "async-notif-creg $ll"
182 proc async-control-max0 {c l allows} {
183 async-control-core $c $l $allows {
188 if {$tw} { set wanted 1 }
191 foreach allow $allows { lappend $send [lindex $allow 0] }
192 sync-subcommand $c "$cmd=[join $send ,]" async-updated-ok $c
196 proc async-control-cmer {c l allows} {
197 async-control-core $c $l $allows {
200 set mode 0; set ind 0
201 manyset $ca($c) mode keyp disp ind bfr
202 if {$mode==3 && $ind} { set send 3,0,0,1 }
204 sync-subcommand $c "$cmd=$send" async-updated-ok $c
208 proc async-updated-ok
210 proc async-control-core {c l allows ubody_init ubody_perclient ubody_finish} {
213 if {[regexp {^(AT[^=?])\?$} $l dummy cmd]} {
214 sync-subcommand $c $l async-massage-result-subs $c $cmd
215 } elseif {[regexp {^(AT[^=?])=\?$} $l dummy cmd]} {
216 sync-subcommand $c $l async-massage-result-support $c $cmd $allows
217 } elseif {[regexp {^(AT[^=?])=([0-9,]+)$} $l dummy cmd values]} {
218 set values [split $values ,]
219 if {[llength $values] > [llength $allows]} {
220 bad-command "too many values"
223 while {[llength $values] < [llength $allows]} {
226 foreach val $values allow $allows {
227 if {[lsearch -exact $allow $val]<0} {
228 bad-command "$val not in allowed $allow ($allows)"
232 uplevel 1 [list upvar #0 client_async/$cmd ca]
233 upvar #0 client_async/$cmd ca
235 uplevel 1 $ubody_init
237 foreach uc [array names clients] {
238 uplevel 1 $ubody_perclient
240 uplevel 1 $ubody_finish
242 bad-command "unknown async control syntax"
246 proc set-client-echo {c yn} {
248 set client_echo($c) 0
249 client-command-complete $c OK
252 proc simple-command {c l} {
253 sync-subcommand $c $l simple-command-complete
256 proc client-command-complete
258 proc process-client-command {c nl} {
260 {^AT\+CREG\b} { async-control-max0 $c $l {{0 1 2}} }
261 {^AT\+CGREG\b} { async-control-max0 $c $l {{0 1 2}} }
262 {^AT\*ERINFO\b} { async-control-max0 $c $l {{0 1}} }
263 {^AT\+CGEREP\b} { async-control-max0 $c $l {{0 1 2} 0} }
264 {^AT\+CMER\b} { async-control-cmer $c $l {{0 3} 0 0 {0 1} 0} }
265 {^ATE0$} { set-client-echo $c 0 }
266 {^ATE1$} { set-client-echo $c 1 }
267 {^AT\+CFUN\b} { simple-command $c $l }
268 default { bad-command "unknown command" }
272 proc dchan-line {l} {
275 {\+CREG:} { async-notif-creg +CREG $l }
276 {\+CGREG:} { async-notif-creg +CGREG $l }
277 {\*ERINFO:} { async-notif-or-resp-fixed *ERINFO $l }
278 {\+CGEV:} { async-notif +CGEREP $l }
279 {\+CIEV:} { async-notif +CMER $l }
280 default { syync-reply $l }
284 proc cchan-line {c l} {
285 lappend queue [list $c $l]
289 proc sync-subcommand {c l args} {
291 if {[info exists busy]} { error "already busy $busy; want $c $l $args" }
292 if {[trap-log "write device" { puts $dchan $l } dummy]} {
295 proc sync-reply {l} {
297 if {![info exists busy]} {
298 bad-data $l "unexpected sync reply"
305 while {![info exists busy] && [llength $queue]} {
306 manyset [lindex $queue 0] c l
307 set queue [lrange $queue 1 end]
308 if {[trap-log "process for $c [logquote $l]"] {
309 process-client-command $c $l
311 client-command-complete $c ERROR
316 proc client-command-complete {c l} {
317 if {[trap-log "write to $c" { puts $c $l } dummy]} {
322 proc cchan-readable {