xfer.tcl 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. namespace eval expectnmcu::xfer {
  2. }
  3. package require expectnmcu::core
  4. # Open remote file `which` on `dev` in `mode` as Lua object `dfh`
  5. proc ::expectnmcu::xfer::open { dev dfh which mode } {
  6. ::expectnmcu::core::send_exp_prompt ${dev} "${dfh} = nil"
  7. ::expectnmcu::core::send_exp_prompt ${dev} "${dfh} = file.open(\"${which}\",\"${mode}\")"
  8. ::expectnmcu::core::send_exp_res_prompt ${dev} "=type(${dfh})" "userdata"
  9. }
  10. # Close Lua file object `dfh` on `dev`
  11. proc ::expectnmcu::xfer::close { dev dfh } {
  12. ::expectnmcu::core::send_exp_prompt ${dev} "${dfh}:close()"
  13. }
  14. # Write to `dfh` on `dev` at `where` `what`, using base64 as transport
  15. #
  16. # This does not split lines; write only short amounts of data.
  17. proc ::expectnmcu::xfer::pwrite { dev dfh where what } {
  18. send -i ${dev} -- [string cat \
  19. "do local d,e = encoder.fromBase64(\"[binary encode base64 -maxlen 0 ${what}]\");" \
  20. "${dfh}:seek(\"set\",${where});" \
  21. "print(${dfh}:write(d));" \
  22. "end\n" \
  23. ]
  24. expect {
  25. -i ${dev} -re "true\[\r\n\]+> " { }
  26. -i ${dev} -re ${::expectnmcu::core::panicre} { return -code error "Panic!" }
  27. -i ${dev} -ex "\n> " { return -code error "Bad result from pwrite" }
  28. timeout { return -code error "Timeout while waiting for pwrite" }
  29. }
  30. }
  31. # Read `howmuch` byetes from `dfh` on `dev` at `where`, using base64
  32. # as transport. This buffers the whole data and its base64 encoding
  33. # in device RAM; read only short strings.
  34. proc ::expectnmcu::xfer::pread { dev dfh where howmuch } {
  35. send -i ${dev} -- "${dfh}:seek(\"set\",${where}); print(encoder.toBase64(${dfh}:read(${howmuch})))\n"
  36. expect {
  37. -i ${dev} -re "\\)\\)\\)\[\r\n\]+(\[^\r\n\]+)\[\r\n\]+> " {
  38. return [binary decode base64 ${expect_out(1,string)}]
  39. }
  40. -i ${dev} -ex "\n> " { return -code error "No reply to pread" }
  41. -i ${dev} -re ${::expectnmcu::core::panicre} { return -code error "Panic!" }
  42. timeout { return -code error "Timeout while pread-ing" }
  43. }
  44. }
  45. # Check for pipeutils on the target device
  46. proc ::expectnmcu::xfer::haspipeutils { dev } {
  47. send -i ${dev} -- "local ok, pu = pcall(require, \"pipeutils\"); print(ok and type(pu) == \"table\" and pu.chunker and pu.debase64 and true or false)\n"
  48. expect {
  49. -i ${dev} -re "\[\r\n\]+false\[\r\n\]+> " { return 0 }
  50. -i ${dev} -re "\[\r\n\]+true\[\r\n\]+> " { return 1 }
  51. -i ${dev} -ex "\n> " { return -code error "No reply to pipeutils probe" }
  52. -i ${dev} -re ${::expectnmcu::core::panicre} { return -code error "Panic!" }
  53. timeout { return -code error "Timeout while probing for pipeutils" }
  54. }
  55. }
  56. # Send local file `lfn` to the remote filesystem on `dev` and name it `rfn`.
  57. # Use `dfo` as the Lua handle to the remote file for the duration of writing,
  58. # (and `nil` it out afterwards)
  59. proc ::expectnmcu::xfer::sendfile { dev lfn rfn {dfo "xfo"} } {
  60. package require sha256
  61. set has_pipeutils [::expectnmcu::xfer::haspipeutils ${dev} ]
  62. set ltf [::open ${lfn} ]
  63. fconfigure ${ltf} -translation binary
  64. file stat ${lfn} lfstat
  65. ::expectnmcu::xfer::open ${dev} ${dfo} "${rfn}.sf" "w+"
  66. if { ${has_pipeutils} } {
  67. # Send over a loader program
  68. ::expectnmcu::core::send_exp_prompt_c ${dev} "do"
  69. ::expectnmcu::core::send_exp_prompt_c ${dev} " local pu = require \"pipeutils\""
  70. ::expectnmcu::core::send_exp_prompt_c ${dev} " local ch = pu.chunker(function(d) ${dfo}:write(d) end, 256)"
  71. ::expectnmcu::core::send_exp_prompt_c ${dev} " local db = pu.debase64(ch.write, function(ed,ee)"
  72. ::expectnmcu::core::send_exp_prompt_c ${dev} " if ed:match(\"^%.\[\\r\\n\]*$\") then ch.flush() print(\"F I N\")"
  73. ::expectnmcu::core::send_exp_prompt_c ${dev} " else print(\"ABORT\", ee, ed) end"
  74. ::expectnmcu::core::send_exp_prompt_c ${dev} " uart.on(\"data\") end)"
  75. # TODO: make echo use CRC not full string; probably best add to crypto module
  76. ::expectnmcu::core::send_exp_prompt_c ${dev} " uart.on(\"data\", \"\\n\", function(x) db.write(x); uart.write(0, \"OK: \", x) end, 0)"
  77. ::expectnmcu::core::send_exp_prompt ${dev} "end"
  78. set xln 90
  79. } else {
  80. set xln 48
  81. }
  82. set lho [sha2::SHA256Init]
  83. set fpos 0
  84. while { 1 } {
  85. send_user ">> xfer ${fpos} of ${lfstat(size)}\n"
  86. set data [read ${ltf} ${xln}]
  87. sha2::SHA256Update ${lho} ${data}
  88. if { ${has_pipeutils} } {
  89. set estr [binary encode base64 -maxlen 0 ${data}]
  90. send -i ${dev} -- "${estr}\n"
  91. expect {
  92. -i ${dev} -ex "OK: ${estr}" { expect -i ${dev} -re "\[\r\n\]+" {} }
  93. -i ${dev} -ex "\n> " { return -code error "Prompt while sending data" }
  94. -i ${dev} -re ${::expectnmcu::core::panicre} { return -code error "Panic!" }
  95. timeout { return -code error "Timeout while sending data" }
  96. }
  97. } else {
  98. ::expectnmcu::xfer::pwrite ${dev} ${dfo} ${fpos} ${data}
  99. }
  100. set fpos [expr $fpos + ${xln}]
  101. if { [string length ${data}] != ${xln} } { break }
  102. }
  103. if { ${has_pipeutils} } {
  104. send -i ${dev} -- ".\n"
  105. expect {
  106. -i ${dev} -re "F I N\[\r\n\]+" { }
  107. -i ${dev} -ex "\n> " { return -code error "Prompt while awaiting acknowledgement" }
  108. -i ${dev} -re ${::expectnmcu::core::panicre} { return -code error "Panic!" }
  109. timeout { return -code error "Timeout while awaiting acknowledgement" }
  110. }
  111. }
  112. ::close ${ltf}
  113. ::expectnmcu::xfer::close ${dev} ${dfo}
  114. ::expectnmcu::core::send_exp_prompt ${dev} "${dfo} = nil"
  115. set exphash [sha2::Hex [sha2::SHA256Final ${lho}]]
  116. send -i ${dev} "=encoder.toHex(crypto.fhash(\"sha256\",\"${rfn}.sf\"))\n"
  117. expect {
  118. -i ${dev} -re "\[\r\n\]+(\[a-f0-9\]+)\[\r\n\]+> " {
  119. if { ${expect_out(1,string)} != ${exphash} } {
  120. return -code error \
  121. "Sendfile checksum mismatch: ${expect_out(1,string)} != ${exphash}"
  122. }
  123. }
  124. -i ${dev} -re ${::expectnmcu::core::panicre} { return -code error "Panic!" }
  125. timeout { return -code error "Timeout while verifying checksum" }
  126. }
  127. ::expectnmcu::core::send_exp_prompt ${dev} "file.remove(\"${rfn}\")"
  128. ::expectnmcu::core::send_exp_res_prompt ${dev} "=file.rename(\"${rfn}.sf\", \"${rfn}\")" "true"
  129. }
  130. package provide expectnmcu::xfer 1.0