tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/examples/sdcard/console/console.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io" 6 "machine" 7 "os" 8 "strconv" 9 "strings" 10 "time" 11 12 "tinygo.org/x/drivers/sdcard" 13 ) 14 15 const consoleBufLen = 64 16 const storageBufLen = 1024 17 18 var ( 19 debug = false 20 21 input [consoleBufLen]byte 22 store [storageBufLen]byte 23 24 console = machine.Serial 25 26 dev *sdcard.Device 27 28 commands map[string]cmdfunc = map[string]cmdfunc{ 29 "": cmdfunc(noop), 30 "help": cmdfunc(help), 31 "dbg": cmdfunc(dbg), 32 "erase": cmdfunc(erase), 33 "lsblk": cmdfunc(lsblk), 34 "write": cmdfunc(write), 35 "xxd": cmdfunc(xxd), 36 } 37 38 his history 39 ) 40 41 type history struct { 42 buf [32]string 43 wp int 44 idx int 45 } 46 47 func (h *history) Add(cmd string) { 48 if len(cmd) == 0 { 49 h.idx = h.wp 50 return 51 } 52 53 if h.wp == len(h.buf)-1 { 54 for i := 1; i < len(h.buf); i++ { 55 h.buf[i-1] = h.buf[i] 56 } 57 h.wp-- 58 } 59 60 h.buf[h.wp] = cmd 61 h.wp++ 62 h.idx = h.wp 63 } 64 65 func (h *history) PeekPrev() string { 66 if h.idx > 0 { 67 h.idx-- 68 } 69 return h.buf[h.idx] 70 } 71 72 func (h *history) PeekNext() string { 73 if h.idx < h.wp { 74 h.idx++ 75 } 76 return h.buf[h.idx] 77 } 78 79 type cmdfunc func(argv []string) 80 81 const ( 82 StateInput = iota 83 StateEscape 84 StateEscBrc 85 StateCSI 86 ) 87 88 func RunFor(device *sdcard.Device) { 89 90 dev = device 91 92 prompt() 93 94 var state = StateInput 95 96 for i := 0; ; { 97 if console.Buffered() > 0 { 98 data, _ := console.ReadByte() 99 if debug { 100 fmt.Printf("\rdata: %x, his.idx: %d\r\n\r", data, his.idx) 101 prompt() 102 console.Write(input[:i]) 103 } 104 switch state { 105 case StateInput: 106 switch data { 107 case 0x8: 108 fallthrough 109 case 0x7f: // this is probably wrong... works on my machine tho :) 110 // backspace 111 if i > 0 { 112 i -= 1 113 console.Write([]byte{0x8, 0x20, 0x8}) 114 } 115 case 13: 116 // return key 117 console.Write([]byte("\r\n")) 118 runCommand(string(input[:i])) 119 his.Add(string(input[:i])) 120 prompt() 121 122 i = 0 123 continue 124 case 27: 125 // escape 126 state = StateEscape 127 default: 128 // anything else, just echo the character if it is printable 129 if strconv.IsPrint(rune(data)) { 130 if i < (consoleBufLen - 1) { 131 console.WriteByte(data) 132 input[i] = data 133 i++ 134 } 135 } 136 } 137 case StateEscape: 138 switch data { 139 case 0x5b: 140 state = StateEscBrc 141 default: 142 state = StateInput 143 } 144 case StateEscBrc: 145 switch data { 146 case 0x41: 147 // up 148 println() 149 prompt() 150 cmd := his.PeekPrev() 151 i = len(cmd) 152 copy(input[:i], []byte(cmd)) 153 console.Write(input[:i]) 154 state = StateInput 155 case 0x42: 156 //down 157 println() 158 prompt() 159 cmd := his.PeekNext() 160 i = len(cmd) 161 copy(input[:i], []byte(cmd)) 162 console.Write(input[:i]) 163 state = StateInput 164 default: 165 // TODO: handle escape sequences 166 state = StateInput 167 } 168 default: 169 // TODO: handle escape sequences 170 state = StateInput 171 } 172 } else { 173 time.Sleep(1 * time.Millisecond) 174 } 175 } 176 } 177 178 func runCommand(line string) { 179 argv := strings.SplitN(strings.TrimSpace(line), " ", -1) 180 cmd := argv[0] 181 182 cmdfn, ok := commands[cmd] 183 if !ok { 184 println("unknown command: " + line) 185 return 186 } 187 cmdfn(argv) 188 } 189 190 func noop(argv []string) {} 191 192 func help(argv []string) { 193 fmt.Printf("help\r\n") 194 fmt.Printf("dbg\r\n") 195 fmt.Printf("erase\r\n") 196 fmt.Printf("lsblk\r\n") 197 fmt.Printf("write <hex offset> <bytes>\r\n") 198 fmt.Printf("xxd <start address> <length>\r\n") 199 } 200 201 func dbg(argv []string) { 202 if debug { 203 debug = false 204 println("Console debbuging off") 205 } else { 206 debug = true 207 println("Console debbuging on") 208 } 209 } 210 211 func lsblk(argv []string) { 212 csd := dev.CSD 213 sectors, err := csd.Sectors() 214 if err != nil { 215 fmt.Printf("%s\r\n", err.Error()) 216 return 217 } 218 cid := dev.CID 219 220 fmt.Printf( 221 "\r\n-------------------------------------\r\n"+ 222 " Device Information: \r\n"+ 223 "-------------------------------------\r\n"+ 224 " JEDEC ID: %v\r\n"+ 225 " Serial: %v\r\n"+ 226 " Status 1: %02x\r\n"+ 227 " Status 2: %02x\r\n"+ 228 " \r\n"+ 229 " Max clock speed (MHz): %d\r\n"+ 230 " Has Sector Protection: %t\r\n"+ 231 " Supports Fast Reads: %t\r\n"+ 232 " Supports QSPI Reads: %t\r\n"+ 233 " Supports QSPI Write: %t\r\n"+ 234 " Write Status Split: %t\r\n"+ 235 " Single Status Byte: %t\r\n"+ 236 "-Sectors: %d\r\n"+ 237 "-Bytes (Sectors * 512) %d\r\n"+ 238 "-ManufacturerID %02X\r\n"+ 239 "-OEMApplicationID %04X\r\n"+ 240 "-ProductName %s\r\n"+ 241 "-ProductVersion %s\r\n"+ 242 "-ProductSerialNumber %08X\r\n"+ 243 "-ManufacturingYear %02X\r\n"+ 244 "-ManufacturingMonth %02X\r\n"+ 245 "-Always1 %d\r\n"+ 246 "-CRC %02X\r\n"+ 247 "-------------------------------------\r\n\r\n", 248 "attrs.JedecID", // attrs.JedecID, 249 cid.ProductSerialNumber, // serialNumber1, 250 0, // status1, 251 0, // status2, 252 csd.TRAN_SPEED, // attrs.MaxClockSpeedMHz, 253 false, // attrs.HasSectorProtection, 254 false, // attrs.SupportsFastRead, 255 false, // attrs.SupportsQSPI, 256 false, // attrs.SupportsQSPIWrites, 257 false, // attrs.WriteStatusSplit, 258 false, // attrs.SingleStatusByte, 259 sectors, 260 csd.Size(), 261 cid.ManufacturerID, 262 cid.OEMApplicationID, 263 cid.ProductName, 264 cid.ProductVersion, 265 cid.ProductSerialNumber, 266 cid.ManufacturingYear, 267 cid.ManufacturingMonth, 268 cid.Always1, 269 cid.CRC, 270 ) 271 } 272 273 func erase(argv []string) { 274 fmt.Printf("erase - not impl\r\n") 275 } 276 277 var writeBuf [256]byte 278 279 func write(argv []string) { 280 if len(argv) < 3 { 281 println("usage: write <hex offset> <bytes>") 282 return 283 } 284 var err error 285 var addr uint64 = 0x0 286 if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil { 287 println("Invalid address: " + err.Error() + "\r\n") 288 return 289 } 290 buf := writeBuf[:0] 291 for i := 0; i < len(argv[2]); i += 2 { 292 var b uint64 293 if b, err = strconv.ParseUint(argv[2][i:i+2], 16, 8); err != nil { 294 println("Invalid bytes: " + err.Error() + "\r\n") 295 return 296 } 297 buf = append(buf, byte(b)) 298 } 299 300 if _, err = dev.WriteAt(buf, int64(addr)); err != nil { 301 println("Write error: " + err.Error() + "\r\n") 302 } 303 } 304 305 func xxd(argv []string) { 306 var err error 307 var addr uint64 = 0x0 308 var size int = 64 309 switch len(argv) { 310 case 3: 311 if size, err = strconv.Atoi(argv[2]); err != nil { 312 println("Invalid size argument: " + err.Error() + "\r\n") 313 return 314 } 315 if size > storageBufLen || size < 1 { 316 fmt.Printf("Size of hexdump must be greater than 0 and less than %d\r\n", storageBufLen) 317 return 318 } 319 fallthrough 320 case 2: 321 if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil { 322 println("Invalid address: " + err.Error() + "\r\n") 323 return 324 } 325 fallthrough 326 case 1: 327 // no args supplied, so nothing to do here, just use the defaults 328 default: 329 println("usage: xxd <hex address, ex: 0xA0> <size of hexdump in bytes>\r\n") 330 return 331 } 332 buf := store[0:size] 333 _, err = dev.ReadAt(buf, int64(addr)) 334 if err != nil { 335 fmt.Printf("xxd err : %s\r\n", err.Error()) 336 } 337 xxdfprint(os.Stdout, uint32(addr), buf) 338 } 339 340 func xxdfprint(w io.Writer, offset uint32, b []byte) { 341 var l int 342 var buf16 = make([]byte, 16) 343 for i, c := 0, len(b); i < c; i += 16 { 344 l = i + 16 345 if l >= c { 346 l = c 347 } 348 fmt.Fprintf(w, "%08x: % x ", offset+uint32(i), b[i:l]) 349 for j, n := 0, l-i; j < 16; j++ { 350 if j >= n || !strconv.IsPrint(rune(b[i+j])) || b[i+j] >= 0x80 { 351 buf16[j] = '.' 352 } else { 353 buf16[j] = b[i+j] 354 } 355 } 356 console.Write(buf16) 357 println() 358 } 359 } 360 361 func prompt() { 362 print("==> ") 363 }