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  }