Add rewritten infinitime abstraction and integrate it into ITD

This commit is contained in:
2024-04-13 21:20:12 -07:00
parent 2a8013e63e
commit 7e68d5541c
31 changed files with 2758 additions and 474 deletions

View File

@@ -0,0 +1,61 @@
package fsproto
import (
"errors"
"fmt"
)
var (
ErrFileNotExists = errors.New("file does not exist")
ErrFileReadOnly = errors.New("file is read only")
ErrFileWriteOnly = errors.New("file is write only")
ErrInvalidOffset = errors.New("offset out of range")
ErrNoRemoveRoot = errors.New("refusing to remove root directory")
)
// Error represents an error returned by BLE FS
type Error struct {
Code int8
}
// Error returns the string associated with the error code
func (err Error) Error() string {
switch err.Code {
case 0x02:
return "filesystem error"
case 0x05:
return "read-only filesystem"
case 0x03:
return "no such file"
case 0x04:
return "protocol error"
case -5:
return "input/output error"
case -84:
return "filesystem is corrupted"
case -2:
return "no such directory entry"
case -17:
return "entry already exists"
case -20:
return "entry is not a directory"
case -39:
return "directory is not empty"
case -9:
return "bad file number"
case -27:
return "file is too large"
case -22:
return "invalid parameter"
case -28:
return "no space left on device"
case -12:
return "no more memory available"
case -61:
return "no attr available"
case -36:
return "file name is too long"
default:
return fmt.Sprintf("unknown error (code %d)", err.Code)
}
}

212
internal/fsproto/fsproto.go Normal file
View File

@@ -0,0 +1,212 @@
package fsproto
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"reflect"
"tinygo.org/x/bluetooth"
)
type FSReqOpcode uint8
const (
ReadFileHeaderOpcode FSReqOpcode = 0x10
ReadFileOpcode FSReqOpcode = 0x12
WriteFileHeaderOpcode FSReqOpcode = 0x20
WriteFileOpcode FSReqOpcode = 0x22
DeleteFileOpcode FSReqOpcode = 0x30
MakeDirectoryOpcode FSReqOpcode = 0x40
ListDirectoryOpcode FSReqOpcode = 0x50
MoveFileOpcode FSReqOpcode = 0x60
)
type FSRespOpcode uint8
const (
ReadFileResp FSRespOpcode = 0x11
WriteFileResp FSRespOpcode = 0x21
DeleteFileResp FSRespOpcode = 0x31
MakeDirectoryResp FSRespOpcode = 0x41
ListDirectoryResp FSRespOpcode = 0x51
MoveFileResp FSRespOpcode = 0x61
)
type ReadFileHeaderRequest struct {
Padding byte
PathLen uint16
Offset uint32
ReadLen uint32
Path string
}
type ReadFileRequest struct {
Status uint8
Padding [2]byte
Offset uint32
ReadLen uint32
}
type ReadFileResponse struct {
Status int8
Padding [2]byte
Offset uint32
FileSize uint32
ChunkLen uint32
Data []byte
}
type WriteFileHeaderRequest struct {
Padding byte
PathLen uint16
Offset uint32
ModTime uint64
FileSize uint32
Path string
}
type WriteFileRequest struct {
Status uint8
Padding [2]byte
Offset uint32
ChunkLen uint32
Data []byte
}
type WriteFileResponse struct {
Status int8
Padding [2]byte
Offset uint32
ModTime uint64
FreeSpace uint32
}
type DeleteFileRequest struct {
Padding byte
PathLen uint16
Path string
}
type DeleteFileResponse struct {
Status int8
}
type MkdirRequest struct {
Padding byte
PathLen uint16
Padding2 [4]byte
Timestamp uint64
Path string
}
type MkdirResponse struct {
Status int8
Padding [6]byte
ModTime uint64
}
type ListDirRequest struct {
Padding byte
PathLen uint16
Path string
}
type ListDirResponse struct {
Status int8
PathLen uint16
EntryNum uint32
TotalEntries uint32
Flags uint32
ModTime uint64
FileSize uint32
Path []byte
}
type MoveFileRequest struct {
Padding byte
OldPathLen uint16
NewPathLen uint16
OldPath string
Padding2 byte
NewPath string
}
type MoveFileResponse struct {
Status int8
}
func WriteRequest(char *bluetooth.DeviceCharacteristic, opcode FSReqOpcode, req any) error {
buf := &bytes.Buffer{}
buf.WriteByte(byte(opcode))
rv := reflect.ValueOf(req)
for rv.Kind() == reflect.Pointer {
rv = rv.Elem()
}
for i := 0; i < rv.NumField(); i++ {
switch field := rv.Field(i); field.Kind() {
case reflect.String:
io.WriteString(buf, field.String())
case reflect.Slice:
if field.Type().Elem().Kind() == reflect.Uint8 {
buf.Write(field.Bytes())
}
default:
binary.Write(buf, binary.LittleEndian, field.Interface())
}
}
_, err := char.WriteWithoutResponse(buf.Bytes())
return err
}
func ReadResponse(b []byte, expect FSRespOpcode, out interface{}) error {
if len(b) == 0 {
return errors.New("empty response packet")
}
if opcode := FSRespOpcode(b[0]); opcode != expect {
return fmt.Errorf("unexpected response opcode: expected %x, got %x", expect, opcode)
}
r := bytes.NewReader(b[1:])
ot := reflect.TypeOf(out)
if ot.Kind() != reflect.Ptr || ot.Elem().Kind() != reflect.Struct {
return errors.New("out parameter must be a pointer to a struct")
}
ov := reflect.ValueOf(out).Elem()
for i := 0; i < ot.Elem().NumField(); i++ {
field := ot.Elem().Field(i)
fieldValue := ov.Field(i)
// If the last field is a byte slice, just read the remaining data into it and return.
if i == ot.Elem().NumField()-1 {
if field.Type.Kind() == reflect.Slice && field.Type.Elem().Kind() == reflect.Uint8 {
data, err := io.ReadAll(r)
if err != nil {
return err
}
fieldValue.SetBytes(data)
return nil
}
}
if err := binary.Read(r, binary.LittleEndian, fieldValue.Addr().Interface()); err != nil {
return err
}
}
if statusField := ov.FieldByName("Status"); !statusField.IsZero() {
code := statusField.Interface().(int8)
if code != 0x01 {
return Error{code}
}
}
return nil
}