| 1 | purpose: Serial terminal emulator |
|---|
| 2 | \ See license at end of file |
|---|
| 3 | |
|---|
| 4 | vocabulary serial-terminal |
|---|
| 5 | also serial-terminal definitions |
|---|
| 6 | |
|---|
| 7 | d# 1 value break-ms |
|---|
| 8 | |
|---|
| 9 | d# 80 constant /buf |
|---|
| 10 | /buf buffer: buf |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | \ queue implementation (adapted from dev/16550pkg/16550.fth) |
|---|
| 14 | |
|---|
| 15 | \ size of queues, approx 10 seconds at 115200 baud |
|---|
| 16 | d# 144000 constant /q |
|---|
| 17 | |
|---|
| 18 | struct |
|---|
| 19 | /n field >head |
|---|
| 20 | /n field >tail |
|---|
| 21 | /q field >qdata |
|---|
| 22 | constant /qstruct |
|---|
| 23 | |
|---|
| 24 | /qstruct buffer: read-q \ for reading from serial |
|---|
| 25 | /qstruct buffer: emit-q \ for showing to display |
|---|
| 26 | |
|---|
| 27 | : init-q ( q -- ) 0 over >head ! 0 swap >tail ! ; |
|---|
| 28 | : inc-q-ptr ( pointer-addr -- ) |
|---|
| 29 | dup @ ca1+ dup /q = if drop 0 then swap ! |
|---|
| 30 | ; |
|---|
| 31 | |
|---|
| 32 | : enque ( new-entry q -- ) |
|---|
| 33 | >r |
|---|
| 34 | r@ >tail @ r@ >head @ 2dup > if - /q then 1- ( entry tail head ) |
|---|
| 35 | <> if r@ >qdata r@ >tail @ ca+ c! r@ >tail inc-q-ptr else drop then |
|---|
| 36 | r> drop |
|---|
| 37 | ; |
|---|
| 38 | |
|---|
| 39 | : deque? ( q -- false | entry true ) |
|---|
| 40 | >r |
|---|
| 41 | r@ >head @ r@ >tail @ <> if |
|---|
| 42 | r@ >qdata r@ >head @ ca+ c@ r@ >head inc-q-ptr true |
|---|
| 43 | else |
|---|
| 44 | false |
|---|
| 45 | then |
|---|
| 46 | r> drop |
|---|
| 47 | ; |
|---|
| 48 | |
|---|
| 49 | \ end of queue implementation |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | \ queued screen output |
|---|
| 53 | |
|---|
| 54 | : >q ( adr len ) |
|---|
| 55 | bounds do i c@ emit-q enque loop |
|---|
| 56 | ; |
|---|
| 57 | |
|---|
| 58 | : q> ( -- ) |
|---|
| 59 | emit-q deque? if emit then |
|---|
| 60 | ; |
|---|
| 61 | |
|---|
| 62 | \ end of queued screen output |
|---|
| 63 | |
|---|
| 64 | |
|---|
| 65 | \ serial device independent interface |
|---|
| 66 | |
|---|
| 67 | defer serial-open ( -- ) |
|---|
| 68 | defer serial-close ( -- ) |
|---|
| 69 | defer serial-emit ( key -- ) |
|---|
| 70 | defer serial-read ( -- adr len ) |
|---|
| 71 | defer serial-break ( -- ) |
|---|
| 72 | |
|---|
| 73 | \ end of serial device independent interface |
|---|
| 74 | |
|---|
| 75 | |
|---|
| 76 | \ internal serial device implementation |
|---|
| 77 | |
|---|
| 78 | \ interrupt enable register UART_IER, table 1993, page 1547 |
|---|
| 79 | : ier@ ( -- b ) h# 1 uart@ ; |
|---|
| 80 | : ier! ( b -- ) h# 1 uart! ; |
|---|
| 81 | |
|---|
| 82 | \ receiver data available interrupt enable |
|---|
| 83 | : ravie-on ( -- ) ier@ h# 1 or ier! ; |
|---|
| 84 | : ravie-off ( -- ) ier@ h# 1 invert and ier! ; |
|---|
| 85 | |
|---|
| 86 | \ line control register UART_LCR, table 1998, page 1554 |
|---|
| 87 | : ulcr@ ( -- b ) h# 3 uart@ ; |
|---|
| 88 | : ulcr! ( b -- ) h# 3 uart! ; |
|---|
| 89 | |
|---|
| 90 | \ set break |
|---|
| 91 | : sb-on ( -- ) ulcr@ h# 40 or ulcr! ; |
|---|
| 92 | : sb-off ( -- ) ulcr@ h# 40 invert and ulcr! ; |
|---|
| 93 | |
|---|
| 94 | \ modem control register UART_MCR, table 1999, page 1555 |
|---|
| 95 | : mcr@ ( -- b ) h# 4 uart@ ; |
|---|
| 96 | : mcr! ( b -- ) h# 4 uart! ; |
|---|
| 97 | |
|---|
| 98 | \ OUT2 signal control, enable UART interrupts |
|---|
| 99 | : out2-on ( -- ) mcr@ h# 8 or mcr! ; |
|---|
| 100 | : out2-off ( -- ) mcr@ h# 8 invert and mcr! ; |
|---|
| 101 | |
|---|
| 102 | : uart-break |
|---|
| 103 | begin uemit? until |
|---|
| 104 | sb-on |
|---|
| 105 | break-ms ms |
|---|
| 106 | sb-off |
|---|
| 107 | ; |
|---|
| 108 | |
|---|
| 109 | \ IRQ number of console UART varies by platform |
|---|
| 110 | \ FIXME: find a better way to store or find these |
|---|
| 111 | [ifdef] olpc-cl1 |
|---|
| 112 | d# 4 value irq# |
|---|
| 113 | [then] |
|---|
| 114 | [ifdef] mmp2 |
|---|
| 115 | d# 24 value irq# |
|---|
| 116 | [then] |
|---|
| 117 | [ifdef] mmp3 |
|---|
| 118 | d# 28 value irq# |
|---|
| 119 | [then] |
|---|
| 120 | |
|---|
| 121 | \ serial interrupt handler for received data |
|---|
| 122 | : si ( -- ) ukey read-q enque ; |
|---|
| 123 | |
|---|
| 124 | \ enable serial interrupt |
|---|
| 125 | : esi |
|---|
| 126 | ['] si irq# interrupt-handler! |
|---|
| 127 | irq# enable-interrupt |
|---|
| 128 | ravie-on |
|---|
| 129 | out2-on |
|---|
| 130 | ; |
|---|
| 131 | |
|---|
| 132 | \ disable serial interrupt |
|---|
| 133 | : dsi |
|---|
| 134 | out2-off |
|---|
| 135 | ravie-off |
|---|
| 136 | irq# disable-interrupt |
|---|
| 137 | ; |
|---|
| 138 | |
|---|
| 139 | \ on XO-1.5, enable-serial disables the camera and adds the serial |
|---|
| 140 | \ instance handles to the multiplexor. on other models it is absent. |
|---|
| 141 | [ifndef] enable-serial \ present on XO-1.5 |
|---|
| 142 | : enable-serial ; |
|---|
| 143 | [then] |
|---|
| 144 | |
|---|
| 145 | false value uart-console-off? \ did we turn our uart console off? |
|---|
| 146 | |
|---|
| 147 | \ stop using the uart as console |
|---|
| 148 | \ (necessary to avoid noise from interconnected hosts) |
|---|
| 149 | : uart-console-off |
|---|
| 150 | fallback-out-ih remove-output |
|---|
| 151 | fallback-in-ih remove-input |
|---|
| 152 | true to uart-console-off? |
|---|
| 153 | ; |
|---|
| 154 | |
|---|
| 155 | \ resume using the uart as console |
|---|
| 156 | : uart-console-on |
|---|
| 157 | uart-console-off? if |
|---|
| 158 | fallback-out-ih add-output |
|---|
| 159 | fallback-in-ih add-input |
|---|
| 160 | false to uart-console-off? |
|---|
| 161 | then |
|---|
| 162 | ; |
|---|
| 163 | |
|---|
| 164 | : uart-open |
|---|
| 165 | enable-serial |
|---|
| 166 | uart-console-off |
|---|
| 167 | read-q init-q |
|---|
| 168 | esi |
|---|
| 169 | ; |
|---|
| 170 | |
|---|
| 171 | : uart-close |
|---|
| 172 | dsi |
|---|
| 173 | ; |
|---|
| 174 | |
|---|
| 175 | : uart-read ( -- adr len ) |
|---|
| 176 | buf 0 ( adr len ) |
|---|
| 177 | read-q deque? 0= if exit then ( adr len char ) |
|---|
| 178 | begin ( adr len char ) |
|---|
| 179 | >r 2dup + r> swap c! 1+ ( adr len' ) |
|---|
| 180 | dup /buf = if exit then ( adr len' ) |
|---|
| 181 | read-q deque? 0= |
|---|
| 182 | until ( adr len' ) |
|---|
| 183 | ; |
|---|
| 184 | |
|---|
| 185 | \ |
|---|
| 186 | \ FIXME: XO-1, seen only once, ukey? did stop returning true, and ukey |
|---|
| 187 | \ therefore hung waiting for ukey? |
|---|
| 188 | \ |
|---|
| 189 | \ condition was cleared by 0 uart@ despite ukey? returning false |
|---|
| 190 | \ |
|---|
| 191 | \ when it occurs again, try looking at fifo error summary bit, and |
|---|
| 192 | \ line status reg, and consider comment from 16550.fth: |
|---|
| 193 | \ |
|---|
| 194 | \ "I have seen conditions where a UART will report, via an interrupt, |
|---|
| 195 | \ that a character is available, but the line status register won't |
|---|
| 196 | \ report it." |
|---|
| 197 | \ |
|---|
| 198 | |
|---|
| 199 | : use-uart |
|---|
| 200 | uart-console-off |
|---|
| 201 | ['] uart-open to serial-open |
|---|
| 202 | ['] uart-close to serial-close |
|---|
| 203 | ['] uemit to serial-emit |
|---|
| 204 | ['] uart-read to serial-read |
|---|
| 205 | ['] uart-break to serial-break |
|---|
| 206 | ; |
|---|
| 207 | |
|---|
| 208 | \ end of internal serial device implementation |
|---|
| 209 | |
|---|
| 210 | |
|---|
| 211 | \ USB serial device implementation |
|---|
| 212 | |
|---|
| 213 | 0 value serial-ih |
|---|
| 214 | |
|---|
| 215 | : usb-open ( -- ) |
|---|
| 216 | " /usb/serial" open-dev ?dup if to serial-ih exit then |
|---|
| 217 | [ifdef] olpc-cl1 |
|---|
| 218 | \ XO-1.5 |
|---|
| 219 | " /usb@10/serial" open-dev ?dup if to serial-ih exit then |
|---|
| 220 | \ XO-1 |
|---|
| 221 | " /usb@f,4/serial" open-dev ?dup if to serial-ih exit then |
|---|
| 222 | " /usb@f,5/serial" open-dev ?dup if to serial-ih exit then |
|---|
| 223 | [then] |
|---|
| 224 | true abort" can't open USB serial adapter" |
|---|
| 225 | ; |
|---|
| 226 | |
|---|
| 227 | : usb-close ( -- ) |
|---|
| 228 | serial-ih close-dev |
|---|
| 229 | 0 to serial-ih |
|---|
| 230 | ; |
|---|
| 231 | |
|---|
| 232 | : usb-emit ( key -- ) |
|---|
| 233 | buf c! buf 1 " write" serial-ih $call-method drop |
|---|
| 234 | ; |
|---|
| 235 | |
|---|
| 236 | : usb-read ( -- adr len ) |
|---|
| 237 | buf /buf " read" serial-ih $call-method ( len ) |
|---|
| 238 | dup -2 = if drop buf 0 exit then ( len ) |
|---|
| 239 | buf swap ( adr len ) |
|---|
| 240 | ; |
|---|
| 241 | |
|---|
| 242 | : usb-break |
|---|
| 243 | " ftdi-break-on" serial-ih $call-method |
|---|
| 244 | 1 ms |
|---|
| 245 | " ftdi-8n1" serial-ih $call-method |
|---|
| 246 | ; |
|---|
| 247 | |
|---|
| 248 | : use-usb |
|---|
| 249 | uart-console-on |
|---|
| 250 | ['] usb-open to serial-open |
|---|
| 251 | ['] usb-close to serial-close |
|---|
| 252 | ['] usb-emit to serial-emit |
|---|
| 253 | ['] usb-read to serial-read |
|---|
| 254 | ['] usb-break to serial-break |
|---|
| 255 | ; |
|---|
| 256 | |
|---|
| 257 | use-usb |
|---|
| 258 | |
|---|
| 259 | \ end of USB serial device implementation |
|---|
| 260 | |
|---|
| 261 | |
|---|
| 262 | \ key bindings |
|---|
| 263 | \ (match the screen(1) defaults) |
|---|
| 264 | defer key-state ( key -- ) |
|---|
| 265 | defer key-state-default ( key -- ) |
|---|
| 266 | |
|---|
| 267 | : reset-key-state ['] key-state-default >data token@ to key-state ; |
|---|
| 268 | |
|---|
| 269 | : key-state-exit ( key -- ) serial-emit ; \ is not called |
|---|
| 270 | |
|---|
| 271 | : key-state-exit? ( -- exit? ) |
|---|
| 272 | ['] key-state >data token@ ['] key-state-exit = |
|---|
| 273 | ; |
|---|
| 274 | |
|---|
| 275 | : key-state-c-a ( key -- ) \ list of recognised c-a sequences |
|---|
| 276 | case |
|---|
| 277 | 1 ( c-a ) of 1 serial-emit reset-key-state endof |
|---|
| 278 | 2 ( c-b ) of serial-break reset-key-state endof |
|---|
| 279 | [char] b of serial-break reset-key-state endof |
|---|
| 280 | [char] C of page reset-key-state endof |
|---|
| 281 | 4 ( c-d ) of ['] key-state-exit to key-state endof |
|---|
| 282 | [char] k of ['] key-state-exit to key-state endof |
|---|
| 283 | [char] K of ['] key-state-exit to key-state endof |
|---|
| 284 | ( default ) reset-key-state |
|---|
| 285 | endcase |
|---|
| 286 | ; |
|---|
| 287 | |
|---|
| 288 | : key-state-run ( key -- ) |
|---|
| 289 | dup 1 = if ['] key-state-c-a to key-state drop exit then \ c-a |
|---|
| 290 | serial-emit ( ) |
|---|
| 291 | ; |
|---|
| 292 | |
|---|
| 293 | ' key-state-run to key-state-default |
|---|
| 294 | |
|---|
| 295 | \ end of key bindings |
|---|
| 296 | |
|---|
| 297 | |
|---|
| 298 | \ main program |
|---|
| 299 | |
|---|
| 300 | : serial-help-0 ( -- ) |
|---|
| 301 | green-letters |
|---|
| 302 | ." serial terminal:" cr |
|---|
| 303 | ." use c-a k to exit," cr |
|---|
| 304 | ." use c-a c-b to send break," cr |
|---|
| 305 | ." use c-a c-a to send a c-a." cr |
|---|
| 306 | cancel cr |
|---|
| 307 | ; |
|---|
| 308 | |
|---|
| 309 | : serial-help-1 ( -- ) |
|---|
| 310 | cr green-letters ." serial terminal: stopped." cancel cr |
|---|
| 311 | ; |
|---|
| 312 | |
|---|
| 313 | : outgoing ( -- ) \ data leaving this host |
|---|
| 314 | key? if key key-state then |
|---|
| 315 | ; |
|---|
| 316 | |
|---|
| 317 | : incoming ( -- ) \ data arriving at this host |
|---|
| 318 | serial-read dup if >q else 2drop q> then |
|---|
| 319 | ; |
|---|
| 320 | |
|---|
| 321 | : serial{ |
|---|
| 322 | emit-q init-q serial-open reset-key-state serial-help-0 |
|---|
| 323 | ; |
|---|
| 324 | |
|---|
| 325 | : {serial} |
|---|
| 326 | begin outgoing incoming key-state-exit? until |
|---|
| 327 | ; |
|---|
| 328 | |
|---|
| 329 | : }serial |
|---|
| 330 | serial-close serial-help-1 |
|---|
| 331 | ; |
|---|
| 332 | |
|---|
| 333 | previous definitions also serial-terminal |
|---|
| 334 | |
|---|
| 335 | : serial serial{ {serial} }serial ; |
|---|
| 336 | |
|---|
| 337 | [ifdef] log-ih |
|---|
| 338 | : serial-log ( "filename" -- ) |
|---|
| 339 | serial{ |
|---|
| 340 | safe-parse-word |
|---|
| 341 | 2dup ['] $delete catch if 2drop then |
|---|
| 342 | $create-file to log-ih |
|---|
| 343 | log-ih add-output |
|---|
| 344 | {serial} |
|---|
| 345 | log-ih remove-output |
|---|
| 346 | log-ih close-dev |
|---|
| 347 | }serial |
|---|
| 348 | ; |
|---|
| 349 | [then] |
|---|
| 350 | |
|---|
| 351 | : use-uart use-uart ; |
|---|
| 352 | : use-usb use-usb ; |
|---|
| 353 | |
|---|
| 354 | previous |
|---|
| 355 | |
|---|
| 356 | \ LICENSE_BEGIN |
|---|
| 357 | \ Copyright (c) 2013 FirmWorks |
|---|
| 358 | \ |
|---|
| 359 | \ Permission is hereby granted, free of charge, to any person obtaining |
|---|
| 360 | \ a copy of this software and associated documentation files (the |
|---|
| 361 | \ "Software"), to deal in the Software without restriction, including |
|---|
| 362 | \ without limitation the rights to use, copy, modify, merge, publish, |
|---|
| 363 | \ distribute, sublicense, and/or sell copies of the Software, and to |
|---|
| 364 | \ permit persons to whom the Software is furnished to do so, subject to |
|---|
| 365 | \ the following conditions: |
|---|
| 366 | \ |
|---|
| 367 | \ The above copyright notice and this permission notice shall be |
|---|
| 368 | \ included in all copies or substantial portions of the Software. |
|---|
| 369 | \ |
|---|
| 370 | \ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|---|
| 371 | \ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|---|
| 372 | \ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|---|
| 373 | \ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|---|
| 374 | \ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|---|
| 375 | \ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|---|
| 376 | \ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|---|
| 377 | \ |
|---|
| 378 | \ LICENSE_END |
|---|