mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 11:07:46 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eafca3c583 | |||
| a01c3423a1 | |||
| 8754f64f3b | |||
| caf405b01d | |||
| bb65c5ce2b | |||
| 283fed16a5 | |||
| 76bba13963 | |||
| ada11090a2 | |||
| a07287d3c6 | |||
| 1b2e764835 | |||
| 2c93a4085c | |||
| 906757603d | |||
| 5d16811199 | |||
| f2e576decf | |||
| 2d61972475 | |||
| 5c488e3145 | |||
| e45ff9c0e8 | |||
| 39805bca45 | |||
| 97842b367c | |||
| 9246899b30 | |||
| 04ace4de5f |
@@ -377,6 +377,8 @@ tinygo-baremetal:
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
$(TINYGO) version
|
||||
# regression test for #2892
|
||||
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
|
||||
# compile-only platform-independent examples
|
||||
cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test
|
||||
# regression test for #2563
|
||||
@@ -396,6 +398,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
||||
@@ -412,6 +416,10 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||
@@ -553,6 +561,11 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
# test usbhid
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(STM32), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
|
||||
+10
-37
@@ -193,7 +193,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
|
||||
Sizes: compiler.Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -439,27 +439,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return errors.New("verification error after interpreting " + pkgInit.Name())
|
||||
}
|
||||
|
||||
// Run function passes for each function in the module.
|
||||
// These passes are intended to be run on each function right
|
||||
// after they're created to reduce IR size (and maybe also for
|
||||
// cache locality to improve performance), but for now they're
|
||||
// run here for each function in turn. Maybe this can be
|
||||
// improved in the future.
|
||||
builder := llvm.NewPassManagerBuilder()
|
||||
defer builder.Dispose()
|
||||
builder.SetOptLevel(optLevel)
|
||||
builder.SetSizeLevel(sizeLevel)
|
||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
||||
defer funcPasses.Dispose()
|
||||
builder.PopulateFunc(funcPasses)
|
||||
funcPasses.InitializeFunc()
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if fn.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
funcPasses.RunFunc(fn)
|
||||
}
|
||||
funcPasses.FinalizeFunc()
|
||||
transform.OptimizePackage(mod, config)
|
||||
|
||||
// Serialize the LLVM module as a bitcode file.
|
||||
// Write to a temporary path that is renamed to the destination
|
||||
@@ -729,6 +709,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
|
||||
}
|
||||
}
|
||||
if config.GOOS() == "darwin" {
|
||||
// Debug information isn't stored in the binary itself on MacOS but
|
||||
// is left in the object files by default. The binary does store the
|
||||
// path to these object files though.
|
||||
return errors.New("cannot remove debug information: MacOS doesn't store debug info in the executable by default")
|
||||
}
|
||||
if config.Target.Linker == "wasm-ld" {
|
||||
// Don't just strip debug information, also compress relocations
|
||||
// while we're at it. Relocations can only be compressed when debug
|
||||
@@ -738,21 +724,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// ld.lld is also used on Linux.
|
||||
ldflags = append(ldflags, "--strip-debug")
|
||||
} else {
|
||||
switch config.GOOS() {
|
||||
case "linux":
|
||||
// Either real linux or an embedded system (like AVR) that
|
||||
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
|
||||
// case (not ld.lld - that case is handled above).
|
||||
ldflags = append(ldflags, "-Wl,--strip-debug")
|
||||
case "darwin":
|
||||
// MacOS (darwin) doesn't have a linker flag to strip debug
|
||||
// information. Apple expects you to use the strip command
|
||||
// instead.
|
||||
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
|
||||
default:
|
||||
// Other OSes may have different flags.
|
||||
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
|
||||
}
|
||||
// Other linkers may have different flags.
|
||||
return errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ var Musl = Library{
|
||||
"internal/libc.c",
|
||||
"internal/syscall_ret.c",
|
||||
"internal/vdso.c",
|
||||
"legacy/*.c",
|
||||
"malloc/*.c",
|
||||
"mman/*.c",
|
||||
"signal/*.c",
|
||||
|
||||
@@ -21,6 +21,10 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
var typeParamUnderlyingType = func(t types.Type) types.Type {
|
||||
return t
|
||||
}
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
llvm.InitializeAllTargetMCs()
|
||||
@@ -335,6 +339,7 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
||||
// getLLVMType instead.
|
||||
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||
goType = typeParamUnderlyingType(goType)
|
||||
switch typ := goType.(type) {
|
||||
case *types.Array:
|
||||
elemType := c.getLLVMType(typ.Elem())
|
||||
@@ -444,6 +449,7 @@ func (c *compilerContext) getDIType(typ types.Type) llvm.Metadata {
|
||||
// createDIType creates a new DWARF type. Don't call this function directly,
|
||||
// call getDIType instead.
|
||||
func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
||||
typ = typeParamUnderlyingType(typ)
|
||||
llvmType := c.getLLVMType(typ)
|
||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||
switch typ := typ.(type) {
|
||||
@@ -794,6 +800,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
for _, method := range methods {
|
||||
// Parse this method.
|
||||
fn := pkg.Prog.MethodValue(method)
|
||||
if fn == nil {
|
||||
continue // probably a generic method
|
||||
}
|
||||
if fn.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package compiler
|
||||
|
||||
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
|
||||
// support.
|
||||
|
||||
import "go/types"
|
||||
|
||||
func init() {
|
||||
typeParamUnderlyingType = func(t types.Type) types.Type {
|
||||
if t, ok := t.(*types.TypeParam); ok {
|
||||
return t.Underlying()
|
||||
}
|
||||
return t
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func TestCompiler(t *testing.T) {
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, "./testdata/"+tc.file, config.ClangHeaders, types.Config{
|
||||
Sizes: Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
+1
-1
@@ -191,7 +191,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
// should be created right away.
|
||||
// The exception is the package initializer, which does appear in the
|
||||
// *ssa.Package members and so shouldn't be created here.
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" {
|
||||
irbuilder := c.ctx.NewBuilder()
|
||||
b := newBuilder(c, irbuilder, fn)
|
||||
b.createFunction()
|
||||
|
||||
@@ -13,7 +13,7 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
go.bug.st/serial v1.1.3
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
||||
golang.org/x/tools v0.1.11
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f
|
||||
)
|
||||
|
||||
@@ -40,41 +40,40 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.bug.st/serial v1.1.3 h1:YEBxJa9pKS9Wdg46B/jiaKbvvbUrjhZZZITfJHEJhaE=
|
||||
go.bug.st/serial v1.1.3/go.mod h1:8TT7u/SwwNIpJ8QaG4s+HTjFt9ReXs2cdOU7ZEk50Dk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 h1:nvvuMxmx1q0gfRki3T0hjG8EwAcVCs91oWAXvyt4zhI=
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
+4
-1
@@ -18,6 +18,7 @@ var (
|
||||
errUnsupportedInst = errors.New("interp: unsupported instruction")
|
||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||
errLoopUnrolled = errors.New("interp: loop unrolled")
|
||||
)
|
||||
|
||||
// This is one of the errors that can be returned from toLLVMValue when the
|
||||
@@ -26,7 +27,9 @@ var (
|
||||
var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
|
||||
|
||||
func isRecoverableError(err error) bool {
|
||||
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
||||
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
||||
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
||||
err == errLoopUnrolled
|
||||
}
|
||||
|
||||
// ErrorLine is one line in a traceback. The position may be missing.
|
||||
|
||||
@@ -24,15 +24,39 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
locals[i] = param
|
||||
}
|
||||
|
||||
// Track what blocks have run instructions at runtime.
|
||||
// This is used to prevent unrolling.
|
||||
var runtimeBlocks map[int]struct{}
|
||||
|
||||
// Start with the first basic block and the first instruction.
|
||||
// Branch instructions may modify both bb and instIndex when branching.
|
||||
bb := fn.blocks[0]
|
||||
currentBB := 0
|
||||
lastBB := -1 // last basic block is undefined, only defined after a branch
|
||||
var operands []value
|
||||
startRTInsts := len(mem.instructions)
|
||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||
if instIndex == 0 {
|
||||
// This is the start of a new basic block.
|
||||
if len(mem.instructions) != startRTInsts {
|
||||
if _, ok := runtimeBlocks[lastBB]; ok {
|
||||
// This loop has been unrolled.
|
||||
// Avoid doing this, as it can result in a large amount of extra machine code.
|
||||
// This currently uses the branch from the last block, as there is no available information to give a better location.
|
||||
lastBBInsts := fn.blocks[lastBB].instructions
|
||||
return nil, mem, r.errorAt(lastBBInsts[len(lastBBInsts)-1], errLoopUnrolled)
|
||||
}
|
||||
|
||||
// Flag the last block as having run stuff at runtime.
|
||||
if runtimeBlocks == nil {
|
||||
runtimeBlocks = make(map[int]struct{})
|
||||
}
|
||||
runtimeBlocks[lastBB] = struct{}{}
|
||||
|
||||
// Reset the block-start runtime instructions counter.
|
||||
startRTInsts = len(mem.instructions)
|
||||
}
|
||||
|
||||
// There may be PHI nodes that need to be resolved. Resolve all PHI
|
||||
// nodes before continuing with regular instructions.
|
||||
// PHI nodes need to be treated specially because they can have a
|
||||
|
||||
Vendored
+45
@@ -3,6 +3,8 @@ target triple = "x86_64--linux"
|
||||
|
||||
declare void @externalCall(i64)
|
||||
|
||||
declare i64 @ptrHash(i8* nocapture)
|
||||
|
||||
@foo.knownAtRuntime = global i64 0
|
||||
@bar.knownAtRuntime = global i64 0
|
||||
@baz.someGlobal = external global [3 x {i64, i32}]
|
||||
@@ -10,6 +12,8 @@ declare void @externalCall(i64)
|
||||
@x.atomicNum = global i32 0
|
||||
@x.volatileNum = global i32 0
|
||||
@y.ready = global i32 0
|
||||
@z.bloom = global i64 0
|
||||
@z.arr = global [32 x i8] zeroinitializer
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
@@ -19,6 +23,7 @@ entry:
|
||||
call void @main.init(i8* undef)
|
||||
call void @x.init(i8* undef)
|
||||
call void @y.init(i8* undef)
|
||||
call void @z.init(i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -72,3 +77,43 @@ loop:
|
||||
end:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @z.init(i8* %context) unnamed_addr {
|
||||
%bloom = bitcast i64* @z.bloom to i8*
|
||||
|
||||
; This can be safely expanded.
|
||||
call void @z.setArr(i8* %bloom, i64 1, i8* %bloom)
|
||||
|
||||
; This call should be reverted to prevent unrolling.
|
||||
call void @z.setArr(i8* bitcast ([32 x i8]* @z.arr to i8*), i64 32, i8* %bloom)
|
||||
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @z.setArr(i8* %arr, i64 %n, i8* %context) unnamed_addr {
|
||||
entry:
|
||||
br label %loop
|
||||
|
||||
loop:
|
||||
%prev = phi i64 [ %n, %entry ], [ %idx, %loop ]
|
||||
%idx = sub i64 %prev, 1
|
||||
%elem = getelementptr i8, i8* %arr, i64 %idx
|
||||
call void @z.set(i8* %elem, i8* %context)
|
||||
%done = icmp eq i64 %idx, 0
|
||||
br i1 %done, label %end, label %loop
|
||||
|
||||
end:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @z.set(i8* %ptr, i8* %context) unnamed_addr {
|
||||
; Insert the pointer into the Bloom filter.
|
||||
%hash = call i64 @ptrHash(i8* %ptr)
|
||||
%index = lshr i64 %hash, 58
|
||||
%bit = shl i64 1, %index
|
||||
%bloom = bitcast i8* %context to i64*
|
||||
%old = load i64, i64* %bloom
|
||||
%new = or i64 %old, %bit
|
||||
store i64 %new, i64* %bloom
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
+33
@@ -8,9 +8,13 @@ target triple = "x86_64--linux"
|
||||
@x.atomicNum = local_unnamed_addr global i32 0
|
||||
@x.volatileNum = global i32 0
|
||||
@y.ready = local_unnamed_addr global i32 0
|
||||
@z.bloom = global i64 0
|
||||
@z.arr = global [32 x i8] zeroinitializer
|
||||
|
||||
declare void @externalCall(i64) local_unnamed_addr
|
||||
|
||||
declare i64 @ptrHash(i8* nocapture) local_unnamed_addr
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call fastcc void @baz.init(i8* undef)
|
||||
@@ -24,6 +28,8 @@ entry:
|
||||
%y = load volatile i32, i32* @x.volatileNum, align 4
|
||||
store volatile i32 %y, i32* @x.volatileNum, align 4
|
||||
call fastcc void @y.init(i8* undef)
|
||||
call fastcc void @z.set(i8* bitcast (i64* @z.bloom to i8*), i8* bitcast (i64* @z.bloom to i8*))
|
||||
call fastcc void @z.setArr(i8* getelementptr inbounds ([32 x i8], [32 x i8]* @z.arr, i32 0, i32 0), i64 32, i8* bitcast (i64* @z.bloom to i8*))
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -48,3 +54,30 @@ loop: ; preds = %loop, %entry
|
||||
end: ; preds = %loop
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal fastcc void @z.setArr(i8* %arr, i64 %n, i8* %context) unnamed_addr {
|
||||
entry:
|
||||
br label %loop
|
||||
|
||||
loop: ; preds = %loop, %entry
|
||||
%prev = phi i64 [ %n, %entry ], [ %idx, %loop ]
|
||||
%idx = sub i64 %prev, 1
|
||||
%elem = getelementptr i8, i8* %arr, i64 %idx
|
||||
call fastcc void @z.set(i8* %elem, i8* %context)
|
||||
%done = icmp eq i64 %idx, 0
|
||||
br i1 %done, label %end, label %loop
|
||||
|
||||
end: ; preds = %loop
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal fastcc void @z.set(i8* %ptr, i8* %context) unnamed_addr {
|
||||
%hash = call i64 @ptrHash(i8* %ptr)
|
||||
%index = lshr i64 %hash, 58
|
||||
%bit = shl i64 1, %index
|
||||
%bloom = bitcast i8* %context to i64*
|
||||
%old = load i64, i64* %bloom, align 8
|
||||
%new = or i64 %old, %bit
|
||||
store i64 %new, i64* %bloom, align 8
|
||||
ret void
|
||||
}
|
||||
+7
-2
@@ -28,6 +28,8 @@ import (
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var addInstances func(*types.Info)
|
||||
|
||||
// Program holds all packages and some metadata about the program as a whole.
|
||||
type Program struct {
|
||||
config *compileopts.Config
|
||||
@@ -104,7 +106,7 @@ type EmbedFile struct {
|
||||
// Load loads the given package with all dependencies (including the runtime
|
||||
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
||||
// processing, if necessary).
|
||||
func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
||||
func Load(config *compileopts.Config, inputPkg string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
||||
goroot, err := GetCachedGoroot(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -133,7 +135,7 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
if config.TestConfig.CompileTestBinary {
|
||||
extraArgs = append(extraArgs, "-test")
|
||||
}
|
||||
cmd, err := List(config, extraArgs, inputPkgs)
|
||||
cmd, err := List(config, extraArgs, []string{inputPkg})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -164,6 +166,9 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||
},
|
||||
}
|
||||
if addInstances != nil {
|
||||
addInstances(&pkg.info)
|
||||
}
|
||||
err := decoder.Decode(&pkg.PackageJSON)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package loader
|
||||
|
||||
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
|
||||
// support.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
addInstances = func(info *types.Info) {
|
||||
info.Instances = make(map[*ast.Ident]types.Instance)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
//
|
||||
// The program must already be parsed and type-checked with the .Parse() method.
|
||||
func (p *Program) LoadSSA() *ssa.Program {
|
||||
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
||||
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug|ssa.InstantiateGenerics)
|
||||
|
||||
for _, pkg := range p.sorted {
|
||||
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
@@ -1252,6 +1253,35 @@ func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
|
||||
return map[string]map[string]string(globalVarValues), nil
|
||||
}
|
||||
|
||||
// getListOfPackages returns a standard list of packages for a given list that might
|
||||
// include wildards using `go list`.
|
||||
// For example [./...] => ["pkg1", "pkg1/pkg12", "pkg2"]
|
||||
func getListOfPackages(pkgs []string, options *compileopts.Options) ([]string, error) {
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd, err := loader.List(config, nil, pkgs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to run `go list`: %w", err)
|
||||
}
|
||||
outputBuf := bytes.NewBuffer(nil)
|
||||
cmd.Stdout = outputBuf
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pkgNames []string
|
||||
sc := bufio.NewScanner(outputBuf)
|
||||
for sc.Scan() {
|
||||
pkgNames = append(pkgNames, sc.Text())
|
||||
}
|
||||
|
||||
return pkgNames, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||
@@ -1487,14 +1517,21 @@ func main() {
|
||||
if len(pkgNames) == 0 {
|
||||
pkgNames = []string{"."}
|
||||
}
|
||||
if outpath != "" && len(pkgNames) > 1 {
|
||||
|
||||
explicitPkgNames, err := getListOfPackages(pkgNames, options)
|
||||
if err != nil {
|
||||
fmt.Printf("cannot resolve packages: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if outpath != "" && len(explicitPkgNames) > 1 {
|
||||
fmt.Println("cannot use -o flag with multiple packages")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fail := make(chan struct{}, 1)
|
||||
var wg sync.WaitGroup
|
||||
bufs := make([]testOutputBuf, len(pkgNames))
|
||||
bufs := make([]testOutputBuf, len(explicitPkgNames))
|
||||
for i := range bufs {
|
||||
bufs[i].done = make(chan struct{})
|
||||
}
|
||||
@@ -1520,7 +1557,7 @@ func main() {
|
||||
// Build and run the tests concurrently.
|
||||
// This uses an additional semaphore to reduce the memory usage.
|
||||
testSema := make(chan struct{}, cap(options.Semaphore))
|
||||
for i, pkgName := range pkgNames {
|
||||
for i, pkgName := range explicitPkgNames {
|
||||
pkgName := pkgName
|
||||
buf := &bufs[i]
|
||||
testSema <- struct{}{}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -75,6 +76,7 @@ func TestBuild(t *testing.T) {
|
||||
tests = append(tests, "go1.17.go")
|
||||
}
|
||||
if minor >= 18 {
|
||||
tests = append(tests, "generics.go")
|
||||
tests = append(tests, "testing_go118.go")
|
||||
} else {
|
||||
tests = append(tests, "testing.go")
|
||||
@@ -520,6 +522,46 @@ func ioLogger(t *testing.T, wg *sync.WaitGroup) io.WriteCloser {
|
||||
return w
|
||||
}
|
||||
|
||||
func TestGetListOfPackages(t *testing.T) {
|
||||
opts := optionsFromTarget("", sema)
|
||||
tests := []struct {
|
||||
pkgs []string
|
||||
expectedPkgs []string
|
||||
expectesError bool
|
||||
}{
|
||||
{
|
||||
pkgs: []string{"./tests/testing/recurse/..."},
|
||||
expectedPkgs: []string{
|
||||
"github.com/tinygo-org/tinygo/tests/testing/recurse",
|
||||
"github.com/tinygo-org/tinygo/tests/testing/recurse/subdir",
|
||||
},
|
||||
},
|
||||
{
|
||||
pkgs: []string{"./tests/testing/pass"},
|
||||
expectedPkgs: []string{
|
||||
"github.com/tinygo-org/tinygo/tests/testing/pass",
|
||||
},
|
||||
},
|
||||
{
|
||||
pkgs: []string{"./tests/testing"},
|
||||
expectesError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
actualPkgs, err := getListOfPackages(test.pkgs, &opts)
|
||||
if err != nil && !test.expectesError {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
} else if err == nil && test.expectesError {
|
||||
t.Error("expected error, but got none")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.expectedPkgs, actualPkgs) {
|
||||
t.Errorf("expected two slices to be equal, expected %v got %v", test.expectedPkgs, actualPkgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
||||
// LLVM tools in a separate process. Not capturing these invocations would lead
|
||||
// to recursive tests.
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// This is a echo console running on the os.Stdin and os.Stdout.
|
||||
// Stdin and os.Stdout are connected to machine.Serial in the baremetal target.
|
||||
//
|
||||
// Serial can be switched with the -serial option as follows
|
||||
// 1. tinygo flash -target wioterminal -serial usb examples/echo2
|
||||
// 2. tinygo flash -target wioterminal -serial uart examples/echo2
|
||||
//
|
||||
// This example will also work with standard Go.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Echo console enabled. Type something then press enter:\r\n")
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
||||
for {
|
||||
msg := ""
|
||||
fmt.Scanf("%s\n", &msg)
|
||||
fmt.Printf("You typed (scanf) : %s\r\n", msg)
|
||||
|
||||
if scanner.Scan() {
|
||||
fmt.Printf("You typed (scanner) : %s\r\n", scanner.Text())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb/hid/keyboard"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
button := machine.BUTTON
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
|
||||
kb := keyboard.New()
|
||||
|
||||
for {
|
||||
if !button.Get() {
|
||||
kb.Write([]byte("tinygo"))
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb/hid/mouse"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
button := machine.BUTTON
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
|
||||
mouse := mouse.New()
|
||||
|
||||
for {
|
||||
if !button.Get() {
|
||||
for j := 0; j < 5; j++ {
|
||||
for i := 0; i < 100; i++ {
|
||||
mouse.Move(1, 0)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
mouse.Move(0, 1)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
mouse.Move(-1, -1)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"machine/usb/midi"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
button := machine.BUTTON
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
|
||||
m := midi.New()
|
||||
m.SetCallback(func(b []byte) {
|
||||
led.Set(!led.Get())
|
||||
fmt.Printf("% X\r\n", b)
|
||||
m.Write(b)
|
||||
})
|
||||
|
||||
prev := true
|
||||
chords := []struct {
|
||||
name string
|
||||
keys []byte
|
||||
}{
|
||||
{name: "C ", keys: []byte{60, 64, 67}},
|
||||
{name: "G ", keys: []byte{55, 59, 62}},
|
||||
{name: "Am", keys: []byte{57, 60, 64}},
|
||||
{name: "F ", keys: []byte{53, 57, 60}},
|
||||
}
|
||||
index := 0
|
||||
|
||||
for {
|
||||
current := button.Get()
|
||||
if prev != current {
|
||||
led.Set(current)
|
||||
if current {
|
||||
for _, c := range chords[index].keys {
|
||||
m.Write([]byte{0x08, 0x80, c, 0x40})
|
||||
}
|
||||
index = (index + 1) % len(chords)
|
||||
} else {
|
||||
for _, c := range chords[index].keys {
|
||||
m.Write([]byte{0x09, 0x90, c, 0x40})
|
||||
}
|
||||
}
|
||||
prev = current
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -60,3 +60,14 @@ const (
|
||||
// Default Serial In Bus 1 for SPI communications
|
||||
SPI1_SDI_PIN = GPIO12 // Rx
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit Feather RP2040"
|
||||
usb_STRING_MANUFACTURER = "Adafruit"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x80F1
|
||||
)
|
||||
|
||||
@@ -49,7 +49,7 @@ const (
|
||||
D36 = PA19 // ESP32 SPI SDO 1[3] PWM EXTI3
|
||||
D37 = NoPin // USB Host enable
|
||||
D38 = PA24 // USB DM
|
||||
D39 = PA27 // USB DP
|
||||
D39 = PA25 // USB DP
|
||||
D40 = PA03 // DAC/VREFP
|
||||
D41 = PB10 // Flash QSPI SCK
|
||||
D42 = PB11 // Flash QSPI CS
|
||||
|
||||
@@ -64,3 +64,14 @@ const (
|
||||
// Default Serial In Bus 1 for SPI communications
|
||||
SPI1_SDI_PIN = GPIO12 // Rx
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Raspberry Pi Pico"
|
||||
usb_STRING_MANUFACTURER = "Raspberry Pi"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2E8A
|
||||
usb_PID uint16 = 0x0003
|
||||
)
|
||||
|
||||
@@ -355,20 +355,20 @@ var (
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA0_PIN = PIN_WIRE_SDA // SDA: SERCOM3/PAD[0]
|
||||
SCL0_PIN = PIN_WIRE_SCL // SCL: SERCOM3/PAD[1]
|
||||
SDA1_PIN = PA17 // SDA: SERCOM3/PAD[0]
|
||||
SCL1_PIN = PA16 // SCL: SERCOM3/PAD[1]
|
||||
|
||||
SDA1_PIN = PIN_WIRE1_SDA // SDA: SERCOM4/PAD[0]
|
||||
SCL1_PIN = PIN_WIRE1_SCL // SCL: SERCOM4/PAD[1]
|
||||
SDA0_PIN = PA13 // SDA: SERCOM4/PAD[0]
|
||||
SCL0_PIN = PA12 // SCL: SERCOM4/PAD[1]
|
||||
|
||||
SDA_PIN = SDA0_PIN
|
||||
SCL_PIN = SCL0_PIN
|
||||
SDA_PIN = SDA1_PIN
|
||||
SCL_PIN = SCL1_PIN
|
||||
)
|
||||
|
||||
// I2C on the Wio Terminal
|
||||
var (
|
||||
I2C0 = sercomI2CM4
|
||||
I2C1 = sercomI2CM4
|
||||
I2C1 = sercomI2CM3
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -1735,889 +1734,6 @@ func (tcc *TCC) Set(channel uint8, value uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
|
||||
type USBCDC struct {
|
||||
Buffer *RingBuffer
|
||||
TxIdx volatile.Register8
|
||||
waitTxc bool
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
configured bool
|
||||
}
|
||||
|
||||
var (
|
||||
USB = &USBCDC{Buffer: NewRingBuffer()}
|
||||
)
|
||||
|
||||
const (
|
||||
usbcdcTxSizeMask uint8 = 0x3F
|
||||
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
|
||||
usbcdcTxBank1st uint8 = 0x00
|
||||
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
|
||||
usbcdcTxMaxRetriesAllowed uint8 = 5
|
||||
)
|
||||
|
||||
// Flush flushes buffered data.
|
||||
func (usbcdc *USBCDC) Flush() error {
|
||||
if usbLineInfo.lineState > 0 {
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
sz := idx & usbcdcTxSizeMask
|
||||
bk := idx & usbcdcTxBankMask
|
||||
if 0 < sz {
|
||||
|
||||
if usbcdc.waitTxc {
|
||||
// waiting for the next flush(), because the transmission is not complete
|
||||
usbcdc.waitTxcRetryCount++
|
||||
return nil
|
||||
}
|
||||
usbcdc.waitTxc = true
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
|
||||
// set the data
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk]))))
|
||||
if bk == usbcdcTxBank1st {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
|
||||
} else {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank1st)
|
||||
}
|
||||
|
||||
// clean multi packet size of bytes already sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set count of bytes to be sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
usbcdc.sent = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the USB CDC interface.
|
||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||
// Supposedly to handle problem with Windows USB serial ports?
|
||||
if usbLineInfo.lineState > 0 {
|
||||
ok := false
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
|
||||
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
|
||||
usbcdc.TxIdx.Set(idx + 1)
|
||||
ok = true
|
||||
}
|
||||
|
||||
interrupt.Restore(mask)
|
||||
|
||||
if ok {
|
||||
break
|
||||
} else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount {
|
||||
mask := interrupt.Disable()
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
usbcdc.TxIdx.Set(0)
|
||||
usbLineInfo.lineState = 0
|
||||
interrupt.Restore(mask)
|
||||
break
|
||||
} else {
|
||||
mask := interrupt.Disable()
|
||||
if usbcdc.sent {
|
||||
if usbcdc.waitTxc {
|
||||
if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) != 0 {
|
||||
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.Flush()
|
||||
}
|
||||
} else {
|
||||
usbcdc.Flush()
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) DTR() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) RTS() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
||||
}
|
||||
|
||||
const (
|
||||
// these are SAMD21 specific.
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
|
||||
|
||||
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
|
||||
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
|
||||
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||
)
|
||||
|
||||
var (
|
||||
usbEndpointDescriptors [8]usbDeviceDescriptor
|
||||
|
||||
udd_ep_in_cache_buffer [7][128]uint8
|
||||
udd_ep_out_cache_buffer [7][128]uint8
|
||||
|
||||
isEndpointHalt = false
|
||||
isRemoteWakeUpEnabled = false
|
||||
endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
|
||||
(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointIn)}
|
||||
|
||||
usbConfiguration uint8
|
||||
usbSetInterface uint8
|
||||
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
|
||||
)
|
||||
|
||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
||||
func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||
// reset USB interface
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
|
||||
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
|
||||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
|
||||
|
||||
// configure pins
|
||||
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
|
||||
// performs pad calibration from store fuses
|
||||
handlePadCalibration()
|
||||
|
||||
// run in standby
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
|
||||
|
||||
// set full speed
|
||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
|
||||
// enable interrupt for start of frame
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
|
||||
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
intr := interrupt.New(sam.IRQ_USB, handleUSB)
|
||||
intr.Enable()
|
||||
|
||||
usbcdc.configured = true
|
||||
}
|
||||
|
||||
// Configured returns whether usbcdc is configured or not.
|
||||
func (usbcdc *USBCDC) Configured() bool {
|
||||
return usbcdc.configured
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// This requires registers that are not included in the SVD file.
|
||||
// Modeled after defines from samd21g18a.h and nvmctrl.h:
|
||||
//
|
||||
// #define NVMCTRL_OTP4 0x00806020
|
||||
//
|
||||
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
|
||||
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
|
||||
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
|
||||
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
|
||||
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
|
||||
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
|
||||
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
|
||||
//
|
||||
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
|
||||
calibTransN := uint16(fuse>>13) & uint16(0x1f)
|
||||
calibTransP := uint16(fuse>>18) & uint16(0x1f)
|
||||
calibTrim := uint16(fuse>>23) & uint16(0x7)
|
||||
|
||||
if calibTransN == 0x1f {
|
||||
calibTransN = 5
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
|
||||
|
||||
if calibTransP == 0x1f {
|
||||
calibTransP = 29
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||
|
||||
if calibTrim == 0x7 {
|
||||
calibTrim = 3
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
func handleUSB(intr interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
// End of reset
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
|
||||
// Configure control endpoint
|
||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
||||
|
||||
// Enable Setup-Received interrupt
|
||||
setEPINTENSET(0, sam.USB_DEVICE_EPINTENSET_RXSTP)
|
||||
|
||||
usbConfiguration = 0
|
||||
|
||||
// ack the End-Of-Reset interrupt
|
||||
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
|
||||
}
|
||||
|
||||
// Start of frame
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||
USB.Flush()
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
// Endpoint 0 Setup interrupt
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_RXSTP > 0 {
|
||||
// ack setup received
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_RXSTP)
|
||||
|
||||
// parse setup
|
||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
// Clear the Bank 0 ready flag on Control OUT
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
ok := false
|
||||
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.wIndex == usb_CDC_ACM_INTERFACE {
|
||||
ok = cdcSetup(setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_STALL1 > 0 {
|
||||
// ack the stall
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_STALL1)
|
||||
|
||||
// clear stall request
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_EPINTENCLR_STALL1)
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
if (epFlags&sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 ||
|
||||
(epFlags&sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 {
|
||||
switch i {
|
||||
case usb_CDC_ENDPOINT_OUT:
|
||||
handleEndpoint(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
|
||||
setEPSTATUSCLR(i, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
if i == usb_CDC_ENDPOINT_IN {
|
||||
USB.waitTxc = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// ready for next transfer
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// Control OUT
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// Control IN
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// Prepare OUT endpoint for receive
|
||||
// set multi packet size for expected number of receive bytes on control OUT
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// NAK on endpoint OUT to show we are ready to receive control data
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_BK0RDY)
|
||||
}
|
||||
}
|
||||
|
||||
func handleStandardSetup(setup usbSetup) bool {
|
||||
switch setup.bRequest {
|
||||
case usb_GET_STATUS:
|
||||
buf := []byte{0, 0}
|
||||
|
||||
if setup.bmRequestType != 0 { // endpoint
|
||||
// TODO: actually check if the endpoint in question is currently halted
|
||||
if isEndpointHalt {
|
||||
buf[0] = 1
|
||||
}
|
||||
}
|
||||
|
||||
sendUSBPacket(0, buf)
|
||||
return true
|
||||
|
||||
case usb_CLEAR_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = false
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = false
|
||||
}
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
case usb_SET_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = true
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = true
|
||||
}
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
case usb_SET_ADDRESS:
|
||||
// set packet size 64 with auto Zlp after transfer
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
|
||||
uint32(1<<31)) // autozlp
|
||||
|
||||
// ack the transfer is complete from the request
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
// set bank ready for data
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
sam.USB_DEVICE.DADD.SetBits(setup.wValueL)
|
||||
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
|
||||
|
||||
return true
|
||||
|
||||
case usb_GET_DESCRIPTOR:
|
||||
sendDescriptor(setup)
|
||||
return true
|
||||
|
||||
case usb_SET_DESCRIPTOR:
|
||||
return false
|
||||
|
||||
case usb_GET_CONFIGURATION:
|
||||
buff := []byte{usbConfiguration}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_CONFIGURATION:
|
||||
if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE {
|
||||
for i := 1; i < len(endPoints); i++ {
|
||||
initEndpoint(uint32(i), endPoints[i])
|
||||
}
|
||||
|
||||
usbConfiguration = setup.wValueL
|
||||
|
||||
// Enable interrupt for CDC control messages from host (OUT packet)
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_EPINTENSET_TRCPT1)
|
||||
|
||||
// Enable interrupt for CDC data messages from host
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||
|
||||
sendZlp()
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
case usb_GET_INTERFACE:
|
||||
buff := []byte{usbSetInterface}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_INTERFACE:
|
||||
usbSetInterface = setup.wValueL
|
||||
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
b, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.wValueL
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING || setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
// auto-reset into the bootloader
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
ResetProcessor()
|
||||
} else {
|
||||
// TODO: cancel any reset
|
||||
}
|
||||
sendZlp()
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SEND_BREAK {
|
||||
// TODO: something with this value?
|
||||
// breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
|
||||
// return false;
|
||||
sendZlp()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data)
|
||||
|
||||
// Set endpoint address for sending data
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// clear multi-packet size which is total bytes already sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count, which is total number of bytes to be sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until OUT transfer is completed.
|
||||
timeout = 300000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// get data
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
// get data
|
||||
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
// move to ring buffer
|
||||
for i := 0; i < count; i++ {
|
||||
USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF)))
|
||||
}
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set multi packet size to 64
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
}
|
||||
|
||||
func sendZlp() {
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func epPacketSize(size uint16) uint32 {
|
||||
switch size {
|
||||
case 8:
|
||||
return 0
|
||||
case 16:
|
||||
return 1
|
||||
case 32:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
case 128:
|
||||
return 4
|
||||
case 256:
|
||||
return 5
|
||||
case 512:
|
||||
return 6
|
||||
case 1023:
|
||||
return 7
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPCFG(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPCFG0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPCFG1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPCFG2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPCFG3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPCFG4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPCFG5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPCFG6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPCFG7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func setEPCFG(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPCFG0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPCFG1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPCFG2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPCFG3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPCFG4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPCFG5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPCFG6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPCFG7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPSTATUSCLR0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPSTATUSCLR1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPSTATUSCLR2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPSTATUSCLR3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPSTATUSCLR4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPSTATUSCLR5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPSTATUSCLR6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPSTATUSCLR7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPSTATUSSET0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPSTATUSSET1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPSTATUSSET2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPSTATUSSET3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPSTATUSSET4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPSTATUSSET5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPSTATUSSET6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPSTATUSSET7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getEPSTATUS(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPSTATUS0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPSTATUS1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPSTATUS2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPSTATUS3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPSTATUS4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPSTATUS5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPSTATUS6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPSTATUS7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPINTFLAG(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPINTFLAG0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPINTFLAG1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPINTFLAG2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPINTFLAG3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPINTFLAG4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPINTFLAG5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPINTFLAG6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPINTFLAG7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTFLAG(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTFLAG0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTFLAG1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTFLAG2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTFLAG3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTFLAG4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTFLAG5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTFLAG6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTFLAG7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTENCLR(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTENCLR0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTENCLR1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTENCLR2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTENCLR3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTENCLR4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTENCLR5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTENCLR6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTENCLR7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTENSET0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTENSET1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTENSET2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTENSET3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTENSET4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTENSET5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTENSET6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTENSET7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ResetProcessor should perform a system reset in preperation
|
||||
// to switch to the bootloader to flash new firmware.
|
||||
func ResetProcessor() {
|
||||
|
||||
@@ -0,0 +1,628 @@
|
||||
//go:build sam && atsamd21
|
||||
// +build sam,atsamd21
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// these are SAMD21 specific.
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
|
||||
|
||||
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
|
||||
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
|
||||
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||
)
|
||||
|
||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
// reset USB interface
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
|
||||
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
|
||||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
|
||||
|
||||
// configure pins
|
||||
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
|
||||
// performs pad calibration from store fuses
|
||||
handlePadCalibration()
|
||||
|
||||
// run in standby
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
|
||||
|
||||
// set full speed
|
||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
|
||||
// enable interrupt for start of frame
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
|
||||
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
interrupt.New(sam.IRQ_USB, handleUSBIRQ).Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// This requires registers that are not included in the SVD file.
|
||||
// Modeled after defines from samd21g18a.h and nvmctrl.h:
|
||||
//
|
||||
// #define NVMCTRL_OTP4 0x00806020
|
||||
//
|
||||
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
|
||||
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
|
||||
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
|
||||
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
|
||||
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
|
||||
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
|
||||
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
|
||||
//
|
||||
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
|
||||
calibTransN := uint16(fuse>>13) & uint16(0x1f)
|
||||
calibTransP := uint16(fuse>>18) & uint16(0x1f)
|
||||
calibTrim := uint16(fuse>>23) & uint16(0x7)
|
||||
|
||||
if calibTransN == 0x1f {
|
||||
calibTransN = 5
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
|
||||
|
||||
if calibTransP == 0x1f {
|
||||
calibTransP = 29
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||
|
||||
if calibTrim == 0x7 {
|
||||
calibTrim = 3
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
// End of reset
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
|
||||
// Configure control endpoint
|
||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
||||
|
||||
usbConfiguration = 0
|
||||
|
||||
// ack the End-Of-Reset interrupt
|
||||
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
|
||||
}
|
||||
|
||||
// Start of frame
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
// Endpoint 0 Setup interrupt
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_RXSTP > 0 {
|
||||
// ack setup received
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_RXSTP)
|
||||
|
||||
// parse setup
|
||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
// Clear the Bank 0 ready flag on Control OUT
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
ok := false
|
||||
if (setup.BmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil {
|
||||
ok = callbackUSBSetup[setup.WIndex](setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_STALL1 > 0 {
|
||||
// ack the stall
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_STALL1)
|
||||
|
||||
// clear stall request
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_EPINTENCLR_STALL1)
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 {
|
||||
buf := handleEndpointRx(i)
|
||||
if callbackUSBRx[i] != nil {
|
||||
callbackUSBRx[i](buf)
|
||||
}
|
||||
} else if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 {
|
||||
if callbackUSBTx[i] != nil {
|
||||
callbackUSBTx[i]()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT1)
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// ready for next transfer
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT1)
|
||||
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// Control OUT
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// Control IN
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// Prepare OUT endpoint for receive
|
||||
// set multi packet size for expected number of receive bytes on control OUT
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// NAK on endpoint OUT to show we are ready to receive control data
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_BK0RDY)
|
||||
|
||||
// Enable Setup-Received interrupt
|
||||
setEPINTENSET(0, sam.USB_DEVICE_EPINTENSET_RXSTP)
|
||||
}
|
||||
}
|
||||
|
||||
func handleUSBSetAddress(setup USBSetup) bool {
|
||||
// set packet size 64 with auto Zlp after transfer
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
|
||||
uint32(1<<31)) // autozlp
|
||||
|
||||
// ack the transfer is complete from the request
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
// set bank ready for data
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
sam.USB_DEVICE.DADD.SetBits(setup.WValueL)
|
||||
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data, 0)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(ep, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
l := uint16(len(data))
|
||||
if 0 < maxsize && maxsize < l {
|
||||
l = maxsize
|
||||
}
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
|
||||
|
||||
// Set endpoint address for sending data
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// clear multi-packet size which is total bytes already sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count, which is total number of bytes to be sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until OUT transfer is completed.
|
||||
timeout = 300000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// get data
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpointRx(ep uint32) []byte {
|
||||
// get data
|
||||
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
// move to ring buffer
|
||||
buf := make([]byte, count)
|
||||
copy(buf, udd_ep_out_cache_buffer[ep][:])
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set multi packet size to 64
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
return buf[:count]
|
||||
}
|
||||
|
||||
func SendZlp() {
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func epPacketSize(size uint16) uint32 {
|
||||
switch size {
|
||||
case 8:
|
||||
return 0
|
||||
case 16:
|
||||
return 1
|
||||
case 32:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
case 128:
|
||||
return 4
|
||||
case 256:
|
||||
return 5
|
||||
case 512:
|
||||
return 6
|
||||
case 1023:
|
||||
return 7
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPCFG(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPCFG0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPCFG1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPCFG2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPCFG3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPCFG4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPCFG5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPCFG6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPCFG7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func setEPCFG(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPCFG0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPCFG1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPCFG2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPCFG3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPCFG4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPCFG5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPCFG6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPCFG7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPSTATUSCLR0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPSTATUSCLR1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPSTATUSCLR2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPSTATUSCLR3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPSTATUSCLR4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPSTATUSCLR5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPSTATUSCLR6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPSTATUSCLR7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPSTATUSSET0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPSTATUSSET1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPSTATUSSET2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPSTATUSSET3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPSTATUSSET4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPSTATUSSET5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPSTATUSSET6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPSTATUSSET7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getEPSTATUS(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPSTATUS0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPSTATUS1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPSTATUS2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPSTATUS3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPSTATUS4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPSTATUS5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPSTATUS6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPSTATUS7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPINTFLAG(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPINTFLAG0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPINTFLAG1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPINTFLAG2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPINTFLAG3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPINTFLAG4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPINTFLAG5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPINTFLAG6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPINTFLAG7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTFLAG(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTFLAG0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTFLAG1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTFLAG2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTFLAG3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTFLAG4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTFLAG5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTFLAG6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTFLAG7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTENCLR(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTENCLR0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTENCLR1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTENCLR2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTENCLR3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTENCLR4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTENCLR5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTENCLR6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTENCLR7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTENSET0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTENSET1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTENSET2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTENSET3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTENSET4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTENSET5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTENSET6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTENSET7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -1975,721 +1974,6 @@ func (tcc *TCC) Set(channel uint8, value uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
|
||||
type USBCDC struct {
|
||||
Buffer *RingBuffer
|
||||
TxIdx volatile.Register8
|
||||
waitTxc bool
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
configured bool
|
||||
}
|
||||
|
||||
var (
|
||||
// USB is a USB CDC interface.
|
||||
USB = &USBCDC{Buffer: NewRingBuffer()}
|
||||
)
|
||||
|
||||
const (
|
||||
usbcdcTxSizeMask uint8 = 0x3F
|
||||
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
|
||||
usbcdcTxBank1st uint8 = 0x00
|
||||
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
|
||||
usbcdcTxMaxRetriesAllowed uint8 = 5
|
||||
)
|
||||
|
||||
// Flush flushes buffered data.
|
||||
func (usbcdc *USBCDC) Flush() error {
|
||||
if usbLineInfo.lineState > 0 {
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
sz := idx & usbcdcTxSizeMask
|
||||
bk := idx & usbcdcTxBankMask
|
||||
if 0 < sz {
|
||||
|
||||
if usbcdc.waitTxc {
|
||||
// waiting for the next flush(), because the transmission is not complete
|
||||
usbcdc.waitTxcRetryCount++
|
||||
return nil
|
||||
}
|
||||
usbcdc.waitTxc = true
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
|
||||
// set the data
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk]))))
|
||||
if bk == usbcdcTxBank1st {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
|
||||
} else {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank1st)
|
||||
}
|
||||
|
||||
// clean multi packet size of bytes already sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set count of bytes to be sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
usbcdc.sent = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the USB CDC interface.
|
||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||
// Supposedly to handle problem with Windows USB serial ports?
|
||||
if usbLineInfo.lineState > 0 {
|
||||
ok := false
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
|
||||
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
|
||||
usbcdc.TxIdx.Set(idx + 1)
|
||||
ok = true
|
||||
}
|
||||
|
||||
interrupt.Restore(mask)
|
||||
|
||||
if ok {
|
||||
break
|
||||
} else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount {
|
||||
mask := interrupt.Disable()
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
usbcdc.TxIdx.Set(0)
|
||||
usbLineInfo.lineState = 0
|
||||
interrupt.Restore(mask)
|
||||
break
|
||||
} else {
|
||||
mask := interrupt.Disable()
|
||||
if usbcdc.sent {
|
||||
if usbcdc.waitTxc {
|
||||
if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) != 0 {
|
||||
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.Flush()
|
||||
}
|
||||
} else {
|
||||
usbcdc.Flush()
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) DTR() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) RTS() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
||||
}
|
||||
|
||||
const (
|
||||
// these are SAMD51 specific.
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
|
||||
|
||||
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
|
||||
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
|
||||
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||
)
|
||||
|
||||
var (
|
||||
usbEndpointDescriptors [8]usbDeviceDescriptor
|
||||
|
||||
udd_ep_in_cache_buffer [7][128]uint8
|
||||
udd_ep_out_cache_buffer [7][128]uint8
|
||||
|
||||
isEndpointHalt = false
|
||||
isRemoteWakeUpEnabled = false
|
||||
endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
|
||||
(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointIn)}
|
||||
|
||||
usbConfiguration uint8
|
||||
usbSetInterface uint8
|
||||
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
|
||||
)
|
||||
|
||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
||||
func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||
// reset USB interface
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
|
||||
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
|
||||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
|
||||
|
||||
// configure pins
|
||||
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
|
||||
// performs pad calibration from store fuses
|
||||
handlePadCalibration()
|
||||
|
||||
// run in standby
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
|
||||
|
||||
// set full speed
|
||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
|
||||
// enable interrupt for start of frame
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
|
||||
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ at highest priority
|
||||
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
||||
|
||||
usbcdc.configured = true
|
||||
}
|
||||
|
||||
// Configured returns whether usbcdc is configured or not.
|
||||
func (usbcdc *USBCDC) Configured() bool {
|
||||
return usbcdc.configured
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// This requires registers that are not included in the SVD file.
|
||||
// Modeled after defines from samd21g18a.h and nvmctrl.h:
|
||||
//
|
||||
// #define NVMCTRL_OTP4 0x00806020
|
||||
//
|
||||
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
|
||||
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
|
||||
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
|
||||
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
|
||||
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
|
||||
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
|
||||
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
|
||||
//
|
||||
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
|
||||
calibTransN := uint16(fuse>>13) & uint16(0x1f)
|
||||
calibTransP := uint16(fuse>>18) & uint16(0x1f)
|
||||
calibTrim := uint16(fuse>>23) & uint16(0x7)
|
||||
|
||||
if calibTransN == 0x1f {
|
||||
calibTransN = 5
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
|
||||
|
||||
if calibTransP == 0x1f {
|
||||
calibTransP = 29
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||
|
||||
if calibTrim == 0x7 {
|
||||
calibTrim = 3
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
func handleUSBIRQ(interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
// End of reset
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
|
||||
// Configure control endpoint
|
||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
||||
|
||||
// Enable Setup-Received interrupt
|
||||
setEPINTENSET(0, sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP)
|
||||
|
||||
usbConfiguration = 0
|
||||
|
||||
// ack the End-Of-Reset interrupt
|
||||
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
|
||||
}
|
||||
|
||||
// Start of frame
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||
USB.Flush()
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
// Endpoint 0 Setup interrupt
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP > 0 {
|
||||
// ack setup received
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP)
|
||||
|
||||
// parse setup
|
||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
// Clear the Bank 0 ready flag on Control OUT
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
ok := false
|
||||
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.wIndex == usb_CDC_ACM_INTERFACE {
|
||||
ok = cdcSetup(setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 {
|
||||
// ack the stall
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
|
||||
// clear stall request
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1)
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
if (epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 ||
|
||||
(epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
|
||||
switch i {
|
||||
case usb_CDC_ENDPOINT_OUT:
|
||||
handleEndpoint(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
|
||||
setEPSTATUSCLR(i, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
if i == usb_CDC_ENDPOINT_IN {
|
||||
USB.waitTxc = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// ready for next transfer
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// Control OUT
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// Control IN
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// Prepare OUT endpoint for receive
|
||||
// set multi packet size for expected number of receive bytes on control OUT
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// NAK on endpoint OUT to show we are ready to receive control data
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK0RDY)
|
||||
}
|
||||
}
|
||||
|
||||
func handleStandardSetup(setup usbSetup) bool {
|
||||
switch setup.bRequest {
|
||||
case usb_GET_STATUS:
|
||||
buf := []byte{0, 0}
|
||||
|
||||
if setup.bmRequestType != 0 { // endpoint
|
||||
// TODO: actually check if the endpoint in question is currently halted
|
||||
if isEndpointHalt {
|
||||
buf[0] = 1
|
||||
}
|
||||
}
|
||||
|
||||
sendUSBPacket(0, buf)
|
||||
return true
|
||||
|
||||
case usb_CLEAR_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = false
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = false
|
||||
}
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
case usb_SET_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = true
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = true
|
||||
}
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
case usb_SET_ADDRESS:
|
||||
// set packet size 64 with auto Zlp after transfer
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
|
||||
uint32(1<<31)) // autozlp
|
||||
|
||||
// ack the transfer is complete from the request
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// set bank ready for data
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
sam.USB_DEVICE.DADD.SetBits(setup.wValueL)
|
||||
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
|
||||
|
||||
return true
|
||||
|
||||
case usb_GET_DESCRIPTOR:
|
||||
sendDescriptor(setup)
|
||||
return true
|
||||
|
||||
case usb_SET_DESCRIPTOR:
|
||||
return false
|
||||
|
||||
case usb_GET_CONFIGURATION:
|
||||
buff := []byte{usbConfiguration}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_CONFIGURATION:
|
||||
if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE {
|
||||
for i := 1; i < len(endPoints); i++ {
|
||||
initEndpoint(uint32(i), endPoints[i])
|
||||
}
|
||||
|
||||
usbConfiguration = setup.wValueL
|
||||
|
||||
// Enable interrupt for CDC control messages from host (OUT packet)
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
||||
|
||||
// Enable interrupt for CDC data messages from host
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
sendZlp()
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
case usb_GET_INTERFACE:
|
||||
buff := []byte{usbSetInterface}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_INTERFACE:
|
||||
usbSetInterface = setup.wValueL
|
||||
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
b, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.wValueL
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING || setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
// auto-reset into the bootloader
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
ResetProcessor()
|
||||
} else {
|
||||
// TODO: cancel any reset
|
||||
}
|
||||
sendZlp()
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SEND_BREAK {
|
||||
// TODO: something with this value?
|
||||
// breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
|
||||
// return false;
|
||||
sendZlp()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data)
|
||||
|
||||
// Set endpoint address for sending data
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// clear multi-packet size which is total bytes already sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count, which is total number of bytes to be sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until OUT transfer is completed.
|
||||
timeout = 300000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// get data
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
// get data
|
||||
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
// move to ring buffer
|
||||
for i := 0; i < count; i++ {
|
||||
USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF)))
|
||||
}
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set multi packet size to 64
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
}
|
||||
|
||||
func sendZlp() {
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func epPacketSize(size uint16) uint32 {
|
||||
switch size {
|
||||
case 8:
|
||||
return 0
|
||||
case 16:
|
||||
return 1
|
||||
case 32:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
case 128:
|
||||
return 4
|
||||
case 256:
|
||||
return 5
|
||||
case 512:
|
||||
return 6
|
||||
case 1023:
|
||||
return 7
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPCFG(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Get()
|
||||
}
|
||||
|
||||
func setEPCFG(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSSET.Set(val)
|
||||
}
|
||||
|
||||
func getEPSTATUS(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUS.Get()
|
||||
}
|
||||
|
||||
func getEPINTFLAG(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Get()
|
||||
}
|
||||
|
||||
func setEPINTFLAG(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENCLR(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
|
||||
}
|
||||
|
||||
// ResetProcessor should perform a system reset in preparation
|
||||
// to switch to the bootloader to flash new firmware.
|
||||
func ResetProcessor() {
|
||||
|
||||
@@ -0,0 +1,460 @@
|
||||
//go:build (sam && atsamd51) || (sam && atsame5x)
|
||||
// +build sam,atsamd51 sam,atsame5x
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// these are SAMD51 specific.
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
|
||||
|
||||
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
|
||||
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
|
||||
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||
)
|
||||
|
||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
// reset USB interface
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
|
||||
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
|
||||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
|
||||
|
||||
// configure pins
|
||||
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
|
||||
// performs pad calibration from store fuses
|
||||
handlePadCalibration()
|
||||
|
||||
// run in standby
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
|
||||
|
||||
// set full speed
|
||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
|
||||
// enable interrupt for start of frame
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
|
||||
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// This requires registers that are not included in the SVD file.
|
||||
// Modeled after defines from samd21g18a.h and nvmctrl.h:
|
||||
//
|
||||
// #define NVMCTRL_OTP4 0x00806020
|
||||
//
|
||||
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
|
||||
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
|
||||
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
|
||||
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
|
||||
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
|
||||
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
|
||||
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
|
||||
//
|
||||
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
|
||||
calibTransN := uint16(fuse>>13) & uint16(0x1f)
|
||||
calibTransP := uint16(fuse>>18) & uint16(0x1f)
|
||||
calibTrim := uint16(fuse>>23) & uint16(0x7)
|
||||
|
||||
if calibTransN == 0x1f {
|
||||
calibTransN = 5
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
|
||||
|
||||
if calibTransP == 0x1f {
|
||||
calibTransP = 29
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||
|
||||
if calibTrim == 0x7 {
|
||||
calibTrim = 3
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
// End of reset
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
|
||||
// Configure control endpoint
|
||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
||||
|
||||
usbConfiguration = 0
|
||||
|
||||
// ack the End-Of-Reset interrupt
|
||||
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
|
||||
}
|
||||
|
||||
// Start of frame
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
// Endpoint 0 Setup interrupt
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP > 0 {
|
||||
// ack setup received
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP)
|
||||
|
||||
// parse setup
|
||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
// Clear the Bank 0 ready flag on Control OUT
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
ok := false
|
||||
if (setup.BmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil {
|
||||
ok = callbackUSBSetup[setup.WIndex](setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 {
|
||||
// ack the stall
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
|
||||
// clear stall request
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1)
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 {
|
||||
buf := handleEndpointRx(i)
|
||||
if callbackUSBRx[i] != nil {
|
||||
callbackUSBRx[i](buf)
|
||||
}
|
||||
} else if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
|
||||
if callbackUSBTx[i] != nil {
|
||||
callbackUSBTx[i]()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// ready for next transfer
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
||||
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// Control OUT
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// Control IN
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// Prepare OUT endpoint for receive
|
||||
// set multi packet size for expected number of receive bytes on control OUT
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// NAK on endpoint OUT to show we are ready to receive control data
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK0RDY)
|
||||
|
||||
// Enable Setup-Received interrupt
|
||||
setEPINTENSET(0, sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP)
|
||||
}
|
||||
}
|
||||
|
||||
func handleUSBSetAddress(setup USBSetup) bool {
|
||||
// set packet size 64 with auto Zlp after transfer
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
|
||||
uint32(1<<31)) // autozlp
|
||||
|
||||
// ack the transfer is complete from the request
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// set bank ready for data
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
sam.USB_DEVICE.DADD.SetBits(setup.WValueL)
|
||||
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data, 0)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(ep, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
l := uint16(len(data))
|
||||
if 0 < maxsize && maxsize < l {
|
||||
l = maxsize
|
||||
}
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
|
||||
|
||||
// Set endpoint address for sending data
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// clear multi-packet size which is total bytes already sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count, which is total number of bytes to be sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until OUT transfer is completed.
|
||||
timeout = 300000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// get data
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpointRx(ep uint32) []byte {
|
||||
// get data
|
||||
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
// move to ring buffer
|
||||
buf := make([]byte, count)
|
||||
copy(buf, udd_ep_out_cache_buffer[ep][:])
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set multi packet size to 64
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
return buf[:count]
|
||||
}
|
||||
|
||||
func SendZlp() {
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func epPacketSize(size uint16) uint32 {
|
||||
switch size {
|
||||
case 8:
|
||||
return 0
|
||||
case 16:
|
||||
return 1
|
||||
case 32:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
case 128:
|
||||
return 4
|
||||
case 256:
|
||||
return 5
|
||||
case 512:
|
||||
return 6
|
||||
case 1023:
|
||||
return 7
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPCFG(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Get()
|
||||
}
|
||||
|
||||
func setEPCFG(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSSET.Set(val)
|
||||
}
|
||||
|
||||
func getEPSTATUS(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUS.Get()
|
||||
}
|
||||
|
||||
func getEPINTFLAG(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Get()
|
||||
}
|
||||
|
||||
func setEPINTFLAG(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENCLR(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
|
||||
}
|
||||
+121
-321
@@ -11,142 +11,15 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// USBCDC is the USB CDC aka serial over USB interface on the nRF52840
|
||||
type USBCDC struct {
|
||||
Buffer *RingBuffer
|
||||
interrupt interrupt.Interrupt
|
||||
initcomplete bool
|
||||
TxIdx volatile.Register8
|
||||
waitTxc bool
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
}
|
||||
|
||||
const (
|
||||
usbcdcTxSizeMask uint8 = 0x3F
|
||||
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
|
||||
usbcdcTxBank1st uint8 = 0x00
|
||||
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
|
||||
usbcdcTxMaxRetriesAllowed uint8 = 5
|
||||
)
|
||||
|
||||
// Flush flushes buffered data.
|
||||
func (usbcdc *USBCDC) Flush() error {
|
||||
if usbLineInfo.lineState > 0 {
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
sz := idx & usbcdcTxSizeMask
|
||||
bk := idx & usbcdcTxBankMask
|
||||
if 0 < sz {
|
||||
|
||||
if usbcdc.waitTxc {
|
||||
// waiting for the next flush(), because the transmission is not complete
|
||||
usbcdc.waitTxcRetryCount++
|
||||
return nil
|
||||
}
|
||||
usbcdc.waitTxc = true
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
|
||||
// set the data
|
||||
enterCriticalSection()
|
||||
sendViaEPIn(
|
||||
usb_CDC_ENDPOINT_IN,
|
||||
&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk],
|
||||
int(sz),
|
||||
)
|
||||
if bk == usbcdcTxBank1st {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
|
||||
} else {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank1st)
|
||||
}
|
||||
|
||||
usbcdc.sent = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the USB CDC interface.
|
||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||
// Supposedly to handle problem with Windows USB serial ports?
|
||||
if usbLineInfo.lineState > 0 {
|
||||
ok := false
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
|
||||
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
|
||||
usbcdc.TxIdx.Set(idx + 1)
|
||||
ok = true
|
||||
}
|
||||
|
||||
interrupt.Restore(mask)
|
||||
|
||||
if ok {
|
||||
break
|
||||
} else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount {
|
||||
mask := interrupt.Disable()
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
usbcdc.TxIdx.Set(0)
|
||||
usbLineInfo.lineState = 0
|
||||
interrupt.Restore(mask)
|
||||
break
|
||||
} else {
|
||||
mask := interrupt.Disable()
|
||||
if usbcdc.sent {
|
||||
if usbcdc.waitTxc {
|
||||
if !easyDMABusy.HasBits(1) {
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.Flush()
|
||||
}
|
||||
} else {
|
||||
usbcdc.Flush()
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) DTR() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) RTS() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
||||
}
|
||||
|
||||
var (
|
||||
USB = &_USB
|
||||
_USB = USBCDC{Buffer: NewRingBuffer()}
|
||||
|
||||
usbEndpointDescriptors [8]usbDeviceDescriptor
|
||||
|
||||
udd_ep_in_cache_buffer [7][128]uint8
|
||||
udd_ep_out_cache_buffer [7][128]uint8
|
||||
|
||||
sendOnEP0DATADONE struct {
|
||||
ptr *byte
|
||||
count int
|
||||
ptr *byte
|
||||
count int
|
||||
offset int
|
||||
}
|
||||
isEndpointHalt = false
|
||||
isRemoteWakeUpEnabled = false
|
||||
endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
|
||||
(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointIn)}
|
||||
|
||||
usbConfiguration uint8
|
||||
usbSetInterface uint8
|
||||
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
|
||||
epinen uint32
|
||||
epouten uint32
|
||||
easyDMABusy volatile.Register8
|
||||
epout0data_setlinecoding bool
|
||||
epinen uint32
|
||||
epouten uint32
|
||||
easyDMABusy volatile.Register8
|
||||
)
|
||||
|
||||
// enterCriticalSection is used to protect access to easyDMA - only one thing
|
||||
@@ -166,19 +39,15 @@ func exitCriticalSection() {
|
||||
easyDMABusy.ClearBits(1)
|
||||
}
|
||||
|
||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
||||
func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||
if usbcdc.initcomplete {
|
||||
return
|
||||
}
|
||||
|
||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
// Enable IRQ. Make sure this is higher than the SWI2 interrupt handler so
|
||||
// that it is possible to print to the console from a BLE interrupt. You
|
||||
// shouldn't generally do that but it is useful for debugging and panic
|
||||
// logging.
|
||||
usbcdc.interrupt = interrupt.New(nrf.IRQ_USBD, _USB.handleInterrupt)
|
||||
usbcdc.interrupt.SetPriority(0x40) // interrupt priority 2 (lower number means more important)
|
||||
usbcdc.interrupt.Enable()
|
||||
intr := interrupt.New(nrf.IRQ_USBD, handleUSBIRQ)
|
||||
intr.SetPriority(0x40) // interrupt priority 2 (lower number means more important)
|
||||
intr.Enable()
|
||||
|
||||
// enable USB
|
||||
nrf.USBD.ENABLE.Set(1)
|
||||
@@ -193,14 +62,12 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||
)
|
||||
|
||||
nrf.USBD.USBPULLUP.Set(0)
|
||||
|
||||
usbcdc.initcomplete = true
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
if nrf.USBD.EVENTS_SOF.Get() == 1 {
|
||||
nrf.USBD.EVENTS_SOF.Set(0)
|
||||
usbcdc.Flush()
|
||||
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
@@ -224,25 +91,30 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
||||
if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 {
|
||||
// done sending packet - either need to send another or enter status stage
|
||||
nrf.USBD.EVENTS_EP0DATADONE.Set(0)
|
||||
if epout0data_setlinecoding {
|
||||
nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
nrf.USBD.EPOUT[0].MAXCNT.Set(64)
|
||||
nrf.USBD.TASKS_STARTEPOUT[0].Set(1)
|
||||
return
|
||||
}
|
||||
if sendOnEP0DATADONE.ptr != nil {
|
||||
// previous data was too big for one packet, so send a second
|
||||
ptr := sendOnEP0DATADONE.ptr
|
||||
count := sendOnEP0DATADONE.count
|
||||
if count > usbEndpointPacketSize {
|
||||
sendOnEP0DATADONE.offset += usbEndpointPacketSize
|
||||
sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[0][sendOnEP0DATADONE.offset]
|
||||
count = usbEndpointPacketSize
|
||||
}
|
||||
sendOnEP0DATADONE.count -= count
|
||||
sendViaEPIn(
|
||||
0,
|
||||
sendOnEP0DATADONE.ptr,
|
||||
sendOnEP0DATADONE.count,
|
||||
ptr,
|
||||
count,
|
||||
)
|
||||
|
||||
// clear, so we know we're done
|
||||
sendOnEP0DATADONE.ptr = nil
|
||||
if sendOnEP0DATADONE.count == 0 {
|
||||
sendOnEP0DATADONE.ptr = nil
|
||||
sendOnEP0DATADONE.offset = 0
|
||||
}
|
||||
} else {
|
||||
// no more data, so set status stage
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
SendZlp() // nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -256,12 +128,13 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
||||
setup := parseUSBSetupRegisters()
|
||||
|
||||
ok := false
|
||||
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
if (setup.BmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
if setup.wIndex == usb_CDC_ACM_INTERFACE {
|
||||
ok = cdcSetup(setup)
|
||||
// Class Interface Requests
|
||||
if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil {
|
||||
ok = callbackUSBSetup[setup.WIndex](setup)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,23 +154,16 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
||||
// Check if endpoint has a pending interrupt
|
||||
inDataDone := epDataStatus&(nrf.USBD_EPDATASTATUS_EPIN1<<(i-1)) > 0
|
||||
outDataDone := epDataStatus&(nrf.USBD_EPDATASTATUS_EPOUT1<<(i-1)) > 0
|
||||
if inDataDone || outDataDone {
|
||||
switch i {
|
||||
case usb_CDC_ENDPOINT_OUT:
|
||||
// setup buffer to receive from host
|
||||
if outDataDone {
|
||||
enterCriticalSection()
|
||||
nrf.USBD.EPOUT[i].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[i]))))
|
||||
count := nrf.USBD.SIZE.EPOUT[i].Get()
|
||||
nrf.USBD.EPOUT[i].MAXCNT.Set(count)
|
||||
nrf.USBD.TASKS_STARTEPOUT[i].Set(1)
|
||||
}
|
||||
case usb_CDC_ENDPOINT_IN: //, usb_CDC_ENDPOINT_ACM:
|
||||
if inDataDone {
|
||||
usbcdc.waitTxc = false
|
||||
exitCriticalSection()
|
||||
}
|
||||
if inDataDone {
|
||||
if callbackUSBTx[i] != nil {
|
||||
callbackUSBTx[i]()
|
||||
}
|
||||
} else if outDataDone {
|
||||
enterCriticalSection()
|
||||
nrf.USBD.EPOUT[i].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[i]))))
|
||||
count := nrf.USBD.SIZE.EPOUT[i].Get()
|
||||
nrf.USBD.EPOUT[i].MAXCNT.Set(count)
|
||||
nrf.USBD.TASKS_STARTEPOUT[i].Set(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,38 +172,23 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
||||
for i := 0; i < len(endPoints); i++ {
|
||||
if nrf.USBD.EVENTS_ENDEPOUT[i].Get() > 0 {
|
||||
nrf.USBD.EVENTS_ENDEPOUT[i].Set(0)
|
||||
if i == 0 && epout0data_setlinecoding {
|
||||
epout0data_setlinecoding = false
|
||||
count := int(nrf.USBD.SIZE.EPOUT[0].Get())
|
||||
if count >= 7 {
|
||||
parseUSBLineInfo(udd_ep_out_cache_buffer[0][:count])
|
||||
checkShouldReset()
|
||||
}
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
if i == usb_CDC_ENDPOINT_OUT {
|
||||
usbcdc.handleEndpoint(uint32(i))
|
||||
buf := handleEndpointRx(uint32(i))
|
||||
if callbackUSBRx[i] != nil {
|
||||
callbackUSBRx[i](buf)
|
||||
}
|
||||
exitCriticalSection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseUSBLineInfo(b []byte) {
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
func parseUSBSetupRegisters() usbSetup {
|
||||
return usbSetup{
|
||||
bmRequestType: uint8(nrf.USBD.BMREQUESTTYPE.Get()),
|
||||
bRequest: uint8(nrf.USBD.BREQUEST.Get()),
|
||||
wValueL: uint8(nrf.USBD.WVALUEL.Get()),
|
||||
wValueH: uint8(nrf.USBD.WVALUEH.Get()),
|
||||
wIndex: uint16((nrf.USBD.WINDEXH.Get() << 8) | nrf.USBD.WINDEXL.Get()),
|
||||
wLength: uint16(((nrf.USBD.WLENGTHH.Get() & 0xff) << 8) | (nrf.USBD.WLENGTHL.Get() & 0xff)),
|
||||
func parseUSBSetupRegisters() USBSetup {
|
||||
return USBSetup{
|
||||
BmRequestType: uint8(nrf.USBD.BMREQUESTTYPE.Get()),
|
||||
BRequest: uint8(nrf.USBD.BREQUEST.Get()),
|
||||
WValueL: uint8(nrf.USBD.WVALUEL.Get()),
|
||||
WValueH: uint8(nrf.USBD.WVALUEH.Get()),
|
||||
WIndex: uint16((nrf.USBD.WINDEXH.Get() << 8) | nrf.USBD.WINDEXL.Get()),
|
||||
WLength: uint16(((nrf.USBD.WLENGTHH.Get() & 0xff) << 8) | (nrf.USBD.WLENGTHL.Get() & 0xff)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,131 +214,30 @@ func initEndpoint(ep, config uint32) {
|
||||
enableEPIn(0)
|
||||
enableEPOut(0)
|
||||
nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0)
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
SendZlp() // nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
}
|
||||
|
||||
func handleStandardSetup(setup usbSetup) bool {
|
||||
switch setup.bRequest {
|
||||
case usb_GET_STATUS:
|
||||
buf := []byte{0, 0}
|
||||
// SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data, 0)
|
||||
|
||||
if setup.bmRequestType != 0 { // endpoint
|
||||
if isEndpointHalt {
|
||||
buf[0] = 1
|
||||
}
|
||||
}
|
||||
// clear transfer complete flag
|
||||
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4)
|
||||
|
||||
sendUSBPacket(0, buf)
|
||||
return true
|
||||
|
||||
case usb_CLEAR_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = false
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = false
|
||||
}
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
return true
|
||||
|
||||
case usb_SET_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = true
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = true
|
||||
}
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
return true
|
||||
|
||||
case usb_SET_ADDRESS:
|
||||
// nrf USBD handles this
|
||||
return true
|
||||
|
||||
case usb_GET_DESCRIPTOR:
|
||||
sendDescriptor(setup)
|
||||
return true
|
||||
|
||||
case usb_SET_DESCRIPTOR:
|
||||
return false
|
||||
|
||||
case usb_GET_CONFIGURATION:
|
||||
buff := []byte{usbConfiguration}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_CONFIGURATION:
|
||||
if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE {
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
for i := 1; i < len(endPoints); i++ {
|
||||
initEndpoint(uint32(i), endPoints[i])
|
||||
}
|
||||
|
||||
usbConfiguration = setup.wValueL
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
case usb_GET_INTERFACE:
|
||||
buff := []byte{usbSetInterface}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_INTERFACE:
|
||||
usbSetInterface = setup.wValueL
|
||||
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
return true
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
epout0data_setlinecoding = true
|
||||
nrf.USBD.TASKS_EP0RCVOUT.Set(1)
|
||||
return true
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.wValueL
|
||||
checkShouldReset()
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SEND_BREAK {
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
count := len(data)
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data)
|
||||
if 0 < int(maxsize) && int(maxsize) < count {
|
||||
count = int(maxsize)
|
||||
}
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data[:count])
|
||||
if ep == 0 && count > usbEndpointPacketSize {
|
||||
sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[ep][usbEndpointPacketSize]
|
||||
sendOnEP0DATADONE.offset = usbEndpointPacketSize
|
||||
sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[ep][sendOnEP0DATADONE.offset]
|
||||
sendOnEP0DATADONE.count = count - usbEndpointPacketSize
|
||||
count = usbEndpointPacketSize
|
||||
}
|
||||
@@ -498,20 +248,21 @@ func sendUSBPacket(ep uint32, data []byte) {
|
||||
)
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) handleEndpoint(ep uint32) {
|
||||
func handleEndpointRx(ep uint32) []byte {
|
||||
// get data
|
||||
count := int(nrf.USBD.EPOUT[ep].AMOUNT.Get())
|
||||
|
||||
// move to ring buffer
|
||||
for i := 0; i < count; i++ {
|
||||
usbcdc.Receive(byte(udd_ep_out_cache_buffer[ep][i]))
|
||||
}
|
||||
buf := make([]byte, count)
|
||||
copy(buf, udd_ep_out_cache_buffer[ep][:])
|
||||
|
||||
// set ready for next data
|
||||
nrf.USBD.SIZE.EPOUT[ep].Set(0)
|
||||
|
||||
return buf[:count]
|
||||
}
|
||||
|
||||
func sendZlp() {
|
||||
func SendZlp() {
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
|
||||
@@ -532,3 +283,52 @@ func enableEPIn(ep uint32) {
|
||||
epinen = epinen | (nrf.USBD_EPINEN_IN0 << ep)
|
||||
nrf.USBD.EPINEN.Set(epinen)
|
||||
}
|
||||
|
||||
func handleUSBSetAddress(setup USBSetup) bool {
|
||||
// nrf USBD handles this
|
||||
return true
|
||||
}
|
||||
|
||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
nrf.USBD.TASKS_EP0RCVOUT.Set(1)
|
||||
|
||||
nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
nrf.USBD.EPOUT[0].MAXCNT.Set(64)
|
||||
|
||||
timeout := 300000
|
||||
count := 0
|
||||
for {
|
||||
if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 {
|
||||
nrf.USBD.EVENTS_EP0DATADONE.Set(0)
|
||||
count = int(nrf.USBD.SIZE.EPOUT[0].Get())
|
||||
nrf.USBD.TASKS_STARTEPOUT[0].Set(1)
|
||||
break
|
||||
}
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
timeout = 300000
|
||||
for {
|
||||
if nrf.USBD.EVENTS_ENDEPOUT[0].Get() == 1 {
|
||||
nrf.USBD.EVENTS_ENDEPOUT[0].Set(0)
|
||||
break
|
||||
}
|
||||
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
nrf.USBD.TASKS_EP0RCVOUT.Set(0)
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:count])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
@@ -14,12 +14,10 @@ const (
|
||||
DFU_MAGIC_OTA_RESET = 0xA8
|
||||
)
|
||||
|
||||
// checkShouldReset is called by the USB-CDC implementation to check whether to
|
||||
// reset into the bootloader/OTA and if so, resets the chip appropriately.
|
||||
func checkShouldReset() {
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
EnterUF2Bootloader()
|
||||
}
|
||||
// ResetProcessor should perform a system reset in preparation
|
||||
// to switch to the bootloader to flash new firmware.
|
||||
func ResetProcessor() {
|
||||
EnterUF2Bootloader()
|
||||
}
|
||||
|
||||
// EnterSerialBootloader resets the chip into the serial bootloader. After
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
//go:build rp2040
|
||||
// +build rp2040
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/rp"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// these are rp2040 specific.
|
||||
)
|
||||
|
||||
var (
|
||||
sendOnEP0DATADONE struct {
|
||||
offset int
|
||||
data []byte
|
||||
pid uint32
|
||||
}
|
||||
)
|
||||
|
||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
//// Clear any previous state in dpram just in case
|
||||
//memset(usb_dpram, 0, sizeof(*usb_dpram)); // <1>
|
||||
usbDPSRAM.clear()
|
||||
|
||||
//// Enable USB interrupt at processor
|
||||
//irq_set_enabled(USBCTRL_IRQ, true);
|
||||
rp.USBCTRL_REGS.INTE.Set(0)
|
||||
intr := interrupt.New(rp.IRQ_USBCTRL_IRQ, handleUSBIRQ)
|
||||
intr.SetPriority(0x00)
|
||||
intr.Enable()
|
||||
irqSet(rp.IRQ_USBCTRL_IRQ, true)
|
||||
|
||||
//// Mux the controller to the onboard usb phy
|
||||
//usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
|
||||
rp.USBCTRL_REGS.USB_MUXING.Set(rp.USBCTRL_REGS_USB_MUXING_TO_PHY | rp.USBCTRL_REGS_USB_MUXING_SOFTCON)
|
||||
|
||||
//// Force VBUS detect so the device thinks it is plugged into a host
|
||||
//usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
|
||||
rp.USBCTRL_REGS.USB_PWR.Set(rp.USBCTRL_REGS_USB_PWR_VBUS_DETECT | rp.USBCTRL_REGS_USB_PWR_VBUS_DETECT_OVERRIDE_EN)
|
||||
|
||||
//// Enable the USB controller in device mode.
|
||||
//usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
|
||||
rp.USBCTRL_REGS.MAIN_CTRL.Set(rp.USBCTRL_REGS_MAIN_CTRL_CONTROLLER_EN)
|
||||
|
||||
//// Enable an interrupt per EP0 transaction
|
||||
//usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS; // <2>
|
||||
rp.USBCTRL_REGS.SIE_CTRL.Set(rp.USBCTRL_REGS_SIE_CTRL_EP0_INT_1BUF)
|
||||
|
||||
//// Enable interrupts for when a buffer is done, when the bus is reset,
|
||||
//// and when a setup packet is received
|
||||
//usb_hw->inte = USB_INTS_BUFF_STATUS_BITS |
|
||||
// USB_INTS_BUS_RESET_BITS |
|
||||
// USB_INTS_SETUP_REQ_BITS;
|
||||
rp.USBCTRL_REGS.INTE.Set(rp.USBCTRL_REGS_INTE_BUFF_STATUS |
|
||||
rp.USBCTRL_REGS_INTE_BUS_RESET |
|
||||
rp.USBCTRL_REGS_INTE_SETUP_REQ)
|
||||
|
||||
//// Set up endpoints (endpoint control registers)
|
||||
//// described by device configuration
|
||||
//usb_setup_endpoints();
|
||||
// void usb_setup_endpoints() {
|
||||
// const struct usb_endpoint_configuration *endpoints = dev_config.endpoints;
|
||||
// for (int i = 0; i < USB_NUM_ENDPOINTS; i++) {
|
||||
// if (endpoints[i].descriptor && endpoints[i].handler) {
|
||||
// usb_setup_endpoint(&endpoints[i]);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void usb_setup_endpoint(const struct usb_endpoint_configuration *ep) {
|
||||
// printf("Set up endpoint 0x%x with buffer address 0x%p\n", ep->descriptor->bEndpointAddress, ep->data_buffer);
|
||||
//
|
||||
// // EP0 doesn't have one so return if that is the case
|
||||
// if (!ep->endpoint_control) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Get the data buffer as an offset of the USB controller's DPRAM
|
||||
// uint32_t dpram_offset = usb_buffer_offset(ep->data_buffer);
|
||||
// uint32_t reg = EP_CTRL_ENABLE_BITS
|
||||
// | EP_CTRL_INTERRUPT_PER_BUFFER
|
||||
// | (ep->descriptor->bmAttributes << EP_CTRL_BUFFER_TYPE_LSB)
|
||||
// | dpram_offset;
|
||||
// *ep->endpoint_control = reg;
|
||||
// }
|
||||
|
||||
//// Present full speed device by enabling pull up on DP
|
||||
//usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
||||
rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN)
|
||||
|
||||
// 追加
|
||||
val := uint32(0)
|
||||
val |= 0x00000400
|
||||
usbDPSRAM.EP0OutBufferControl = USBBufferControlRegister(val)
|
||||
}
|
||||
|
||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
status := rp.USBCTRL_REGS.INTS.Get()
|
||||
|
||||
// setup
|
||||
//rp.USBCTRL_REGS.SIE_STATUS.Set(0xFFFFFFFF)
|
||||
|
||||
//if false {
|
||||
// // SOF
|
||||
// x := rp.USBCTRL_REGS.SOF_RD.Get()
|
||||
//}
|
||||
|
||||
// void isr_usbctrl(void) {
|
||||
// // USB interrupt handler
|
||||
// uint32_t status = usb_hw->ints;
|
||||
// uint32_t handled = 0;
|
||||
|
||||
// // Setup packet received
|
||||
// if (status & USB_INTS_SETUP_REQ_BITS) {
|
||||
// handled |= USB_INTS_SETUP_REQ_BITS;
|
||||
// usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
|
||||
// usb_handle_setup_packet();
|
||||
// }
|
||||
// /// \end::isr_setup_packet[]
|
||||
if (status & rp.USBCTRL_REGS_INTS_SETUP_REQ) > 0 {
|
||||
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_SETUP_REC)
|
||||
setup := newUSBSetup(usbDPSRAM.Setup[:])
|
||||
|
||||
ok := false
|
||||
if (setup.BmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil {
|
||||
ok = callbackUSBSetup[setup.WIndex](setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
//setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
//setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
//if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 {
|
||||
// // ack the stall
|
||||
// setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
|
||||
// // clear stall request
|
||||
// setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1)
|
||||
//}
|
||||
}
|
||||
|
||||
// // Buffer status, one or more buffers have completed
|
||||
// if (status & USB_INTS_BUFF_STATUS_BITS) {
|
||||
// handled |= USB_INTS_BUFF_STATUS_BITS;
|
||||
// usb_handle_buff_status();
|
||||
// }
|
||||
if (status & rp.USBCTRL_REGS_INTS_BUFF_STATUS) > 0 {
|
||||
if sendOnEP0DATADONE.offset > 0 {
|
||||
ep := uint32(0)
|
||||
data := sendOnEP0DATADONE.data
|
||||
count := len(data) - sendOnEP0DATADONE.offset
|
||||
if ep == 0 && count > usbEndpointPacketSize {
|
||||
count = usbEndpointPacketSize
|
||||
}
|
||||
sendViaEPIn(ep, data[sendOnEP0DATADONE.offset:], count, 0)
|
||||
sendOnEP0DATADONE.offset += count
|
||||
if sendOnEP0DATADONE.offset == len(data) {
|
||||
sendOnEP0DATADONE.offset = 0
|
||||
}
|
||||
}
|
||||
//s2 := rp.USBCTRL_REGS.BUFF_STATUS.Get()
|
||||
//if (s2 & 0x00000001) > 0 {
|
||||
// // EP0_IN
|
||||
//} else if (s2 & 0x00000002) > 0 {
|
||||
// // EP0_OUT
|
||||
// //sendUSBPacket(0, []byte{}, 0)
|
||||
//}
|
||||
rp.USBCTRL_REGS.BUFF_STATUS.Set(0xFFFFFFFF)
|
||||
}
|
||||
|
||||
// // Bus is reset
|
||||
// if (status & USB_INTS_BUS_RESET_BITS) {
|
||||
// printf("BUS RESET\n");
|
||||
// handled |= USB_INTS_BUS_RESET_BITS;
|
||||
// usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
|
||||
// usb_bus_reset();
|
||||
// }
|
||||
// void usb_bus_reset(void) {
|
||||
// // Set address back to 0
|
||||
// dev_addr = 0;
|
||||
// should_set_address = false;
|
||||
// usb_hw->dev_addr_ctrl = 0;
|
||||
// configured = false;
|
||||
// }
|
||||
if (status & rp.USBCTRL_REGS_INTS_BUS_RESET) > 0 {
|
||||
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_BUS_RESET)
|
||||
}
|
||||
|
||||
//
|
||||
// if (status ^ handled) {
|
||||
// panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
if ep == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
val := uint32(0x80000000) | uint32(0x20000000)
|
||||
offset := (ep-1)*64 + 0x180
|
||||
val |= offset
|
||||
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
val |= 0x0C000000
|
||||
// ep1 in
|
||||
usbDPSRAM.EP1InControl = USBEndpointControlRegister(val)
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
val |= 0x08000000
|
||||
// ep2 out
|
||||
usbDPSRAM.EP2OutControl = USBEndpointControlRegister(val)
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
val |= 0x08000000
|
||||
usbDPSRAM.EP3InControl = USBEndpointControlRegister(val)
|
||||
// ep3 in
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// skip
|
||||
}
|
||||
}
|
||||
|
||||
func handleUSBSetAddress(setup USBSetup) bool {
|
||||
// setup.BmRequestType, setup.BRequest, setup.WValueL, setup.WValueH, setup.WIndex, setup.WLength)
|
||||
sendUSBPacket(0, []byte{}, 0)
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_ACK_REC)
|
||||
for (rp.USBCTRL_REGS.SIE_STATUS.Get() & rp.USBCTRL_REGS_SIE_STATUS_ACK_REC) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
rp.USBCTRL_REGS.ADDR_ENDP.Set(uint32(setup.WValueL) & rp.USBCTRL_REGS_ADDR_ENDP_ADDRESS_Msk)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data, 0)
|
||||
return true
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
count := len(data)
|
||||
if 0 < int(maxsize) && int(maxsize) < count {
|
||||
count = int(maxsize)
|
||||
}
|
||||
|
||||
if ep == 0 {
|
||||
if count > usbEndpointPacketSize {
|
||||
count = usbEndpointPacketSize
|
||||
|
||||
sendOnEP0DATADONE.offset = count
|
||||
sendOnEP0DATADONE.data = data
|
||||
} else {
|
||||
sendOnEP0DATADONE.offset = 0
|
||||
}
|
||||
}
|
||||
|
||||
sendViaEPIn(ep, data, count, 1)
|
||||
}
|
||||
|
||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpointRx(ep uint32) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendZlp() {
|
||||
sendUSBPacket(0, []byte{}, 0)
|
||||
}
|
||||
|
||||
var ep3data0 = true
|
||||
|
||||
func sendViaEPIn(ep uint32, data []byte, count int, pid int) {
|
||||
// void usb_start_transfer(struct usb_endpoint_configuration *ep, uint8_t *buf, uint16_t len) {
|
||||
// // We are asserting that the length is <= 64 bytes for simplicity of the example.
|
||||
// // For multi packet transfers see the tinyusb port.
|
||||
// assert(len <= 64);
|
||||
//
|
||||
// printf("Start transfer of len %d on ep addr 0x%x\n", len, ep->descriptor->bEndpointAddress);
|
||||
//
|
||||
// // Prepare buffer control register value
|
||||
// uint32_t val = len | USB_BUF_CTRL_AVAIL;
|
||||
val := uint32(count) | 0x00000400
|
||||
|
||||
// if (ep_is_tx(ep)) {
|
||||
// // Need to copy the data from the user buffer to the usb memory
|
||||
// memcpy((void *) ep->data_buffer, (void *) buf, len);
|
||||
// DATA0 or DATA1
|
||||
if ep == 3 {
|
||||
if ep3data0 {
|
||||
val |= 0x00002000
|
||||
}
|
||||
ep3data0 = !ep3data0
|
||||
} else if pid == 1 {
|
||||
val |= 0x00002000
|
||||
}
|
||||
|
||||
// // Mark as full
|
||||
// val |= USB_BUF_CTRL_FULL;
|
||||
val |= 0x00008000
|
||||
|
||||
// }
|
||||
// // Set pid and flip for next transfer
|
||||
// val |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
|
||||
// ep->next_pid ^= 1u;
|
||||
//
|
||||
// *ep->buffer_control = val;
|
||||
switch ep & 0x7F {
|
||||
case 0:
|
||||
copy(usbDPSRAM.EP0Buffer0[:], data[:count])
|
||||
usbDPSRAM.EP0InBufferControl = USBBufferControlRegister(val)
|
||||
case 1:
|
||||
copy(usbDPSRAM.EP1Buffer[:], data[:count])
|
||||
usbDPSRAM.EP1InBufferControl = USBBufferControlRegister(val)
|
||||
case 2:
|
||||
usbDPSRAM.EP2OutBufferControl = USBBufferControlRegister(val)
|
||||
case 3:
|
||||
copy(usbDPSRAM.EP3Buffer[:], data[:count])
|
||||
usbDPSRAM.EP3InBufferControl = USBBufferControlRegister(val)
|
||||
default:
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
// ResetProcessor should perform a system reset in preparation
|
||||
// to switch to the bootloader to flash new firmware.
|
||||
func ResetProcessor() {
|
||||
arm.DisableInterrupts()
|
||||
|
||||
//// Perform magic reset into bootloader, as mentioned in
|
||||
//// https://github.com/arduino/ArduinoCore-samd/issues/197
|
||||
//*(*uint32)(unsafe.Pointer(uintptr(0x20000000 + HSRAM_SIZE - 4))) = RESET_MAGIC_VALUE
|
||||
|
||||
arm.SystemReset()
|
||||
}
|
||||
|
||||
type USBDPSRAM struct {
|
||||
Setup [8]byte
|
||||
|
||||
EP1InControl USBEndpointControlRegister // 0x0008
|
||||
EP1OutControl USBEndpointControlRegister // 0x000c
|
||||
EP2InControl USBEndpointControlRegister // 0x0010
|
||||
EP2OutControl USBEndpointControlRegister // 0x0014
|
||||
EP3InControl USBEndpointControlRegister // 0x0018
|
||||
EP3OutControl USBEndpointControlRegister // 0x001c
|
||||
EP4InControl USBEndpointControlRegister // 0x0020
|
||||
EP4OutControl USBEndpointControlRegister // 0x0024
|
||||
EP5InControl USBEndpointControlRegister // 0x0028
|
||||
EP5OutControl USBEndpointControlRegister // 0x002c
|
||||
EP6InControl USBEndpointControlRegister // 0x0030
|
||||
EP6OutControl USBEndpointControlRegister // 0x0034
|
||||
EP7InControl USBEndpointControlRegister // 0x0038
|
||||
EP7OutControl USBEndpointControlRegister // 0x003c
|
||||
EP8InControl USBEndpointControlRegister // 0x0040
|
||||
EP8OutControl USBEndpointControlRegister // 0x0044
|
||||
EP9InControl USBEndpointControlRegister // 0x0048
|
||||
EP9OutControl USBEndpointControlRegister // 0x004c
|
||||
EP10InControl USBEndpointControlRegister // 0x0050
|
||||
EP10OutControl USBEndpointControlRegister // 0x0054
|
||||
EP11InControl USBEndpointControlRegister // 0x0058
|
||||
EP11OutControl USBEndpointControlRegister // 0x005c
|
||||
EP12InControl USBEndpointControlRegister // 0x0060
|
||||
EP12OutControl USBEndpointControlRegister // 0x0064
|
||||
EP13InControl USBEndpointControlRegister // 0x0068
|
||||
EP13OutControl USBEndpointControlRegister // 0x006c
|
||||
EP14InControl USBEndpointControlRegister // 0x0070
|
||||
EP14OutControl USBEndpointControlRegister // 0x0074
|
||||
EP15InControl USBEndpointControlRegister // 0x0078
|
||||
EP15OutControl USBEndpointControlRegister // 0x007c
|
||||
|
||||
EP0InBufferControl USBBufferControlRegister // 0x0080
|
||||
EP0OutBufferControl USBBufferControlRegister // 0x0084
|
||||
EP1InBufferControl USBBufferControlRegister // 0x0088
|
||||
EP1OutBufferControl USBBufferControlRegister // 0x008c
|
||||
EP2InBufferControl USBBufferControlRegister // 0x0090
|
||||
EP2OutBufferControl USBBufferControlRegister // 0x0094
|
||||
EP3InBufferControl USBBufferControlRegister // 0x0098
|
||||
EP3OutBufferControl USBBufferControlRegister // 0x009c
|
||||
EP4InBufferControl USBBufferControlRegister // 0x00a0
|
||||
EP4OutBufferControl USBBufferControlRegister // 0x00a4
|
||||
EP5InBufferControl USBBufferControlRegister // 0x00a8
|
||||
EP5OutBufferControl USBBufferControlRegister // 0x00ac
|
||||
EP6InBufferControl USBBufferControlRegister // 0x00b0
|
||||
EP6OutBufferControl USBBufferControlRegister // 0x00b4
|
||||
EP7InBufferControl USBBufferControlRegister // 0x00b8
|
||||
EP7OutBufferControl USBBufferControlRegister // 0x00bc
|
||||
EP8InBufferControl USBBufferControlRegister // 0x00c0
|
||||
EP8OutBufferControl USBBufferControlRegister // 0x00c4
|
||||
EP9InBufferControl USBBufferControlRegister // 0x00c8
|
||||
EP9OutBufferControl USBBufferControlRegister // 0x00cc
|
||||
EP10InBufferControl USBBufferControlRegister // 0x00d0
|
||||
EP10OutBufferControl USBBufferControlRegister // 0x00d4
|
||||
EP11InBufferControl USBBufferControlRegister // 0x00d8
|
||||
EP11OutBufferControl USBBufferControlRegister // 0x00dc
|
||||
EP12InBufferControl USBBufferControlRegister // 0x00e0
|
||||
EP12OutBufferControl USBBufferControlRegister // 0x00e4
|
||||
EP13InBufferControl USBBufferControlRegister // 0x00e8
|
||||
EP13OutBufferControl USBBufferControlRegister // 0x00ec
|
||||
EP14InBufferControl USBBufferControlRegister // 0x00f0
|
||||
EP14OutBufferControl USBBufferControlRegister // 0x00f4
|
||||
EP15InBufferControl USBBufferControlRegister // 0x00f8
|
||||
EP15OutBufferControl USBBufferControlRegister // 0x00fc
|
||||
|
||||
// offset : 0x100
|
||||
EP0Buffer0 [64]byte
|
||||
EP0Buffer1 [64]byte
|
||||
|
||||
// offset : 0x180 ..
|
||||
EP1Buffer [64]byte
|
||||
EP2Buffer [64]byte
|
||||
EP3Buffer [64]byte
|
||||
EP4Buffer [64]byte
|
||||
EP5Buffer [64]byte
|
||||
EP6Buffer [64]byte
|
||||
EP7Buffer [64]byte
|
||||
}
|
||||
|
||||
type USBEndpointControlRegister uint32
|
||||
type USBBufferControlRegister uint32
|
||||
|
||||
var usbDPSRAM = (*USBDPSRAM)(unsafe.Pointer(uintptr(0x50100000)))
|
||||
|
||||
func (d *USBDPSRAM) clear() {
|
||||
for i := range d.Setup {
|
||||
d.Setup[i] = 0
|
||||
}
|
||||
|
||||
d.EP1InControl = 0
|
||||
d.EP1OutControl = 0
|
||||
d.EP2InControl = 0
|
||||
d.EP2OutControl = 0
|
||||
d.EP3InControl = 0
|
||||
d.EP3OutControl = 0
|
||||
d.EP4InControl = 0
|
||||
d.EP4OutControl = 0
|
||||
d.EP5InControl = 0
|
||||
d.EP5OutControl = 0
|
||||
d.EP6InControl = 0
|
||||
d.EP6OutControl = 0
|
||||
d.EP7InControl = 0
|
||||
d.EP7OutControl = 0
|
||||
d.EP8InControl = 0
|
||||
d.EP8OutControl = 0
|
||||
d.EP9InControl = 0
|
||||
d.EP9OutControl = 0
|
||||
d.EP10InControl = 0
|
||||
d.EP10OutControl = 0
|
||||
d.EP11InControl = 0
|
||||
d.EP11OutControl = 0
|
||||
d.EP12InControl = 0
|
||||
d.EP12OutControl = 0
|
||||
d.EP13InControl = 0
|
||||
d.EP13OutControl = 0
|
||||
d.EP14InControl = 0
|
||||
d.EP14OutControl = 0
|
||||
d.EP15InControl = 0
|
||||
d.EP15OutControl = 0
|
||||
|
||||
d.EP0InBufferControl = 0
|
||||
d.EP0OutBufferControl = 0
|
||||
d.EP1InBufferControl = 0
|
||||
d.EP1OutBufferControl = 0
|
||||
d.EP2InBufferControl = 0
|
||||
d.EP2OutBufferControl = 0
|
||||
d.EP3InBufferControl = 0
|
||||
d.EP3OutBufferControl = 0
|
||||
d.EP4InBufferControl = 0
|
||||
d.EP4OutBufferControl = 0
|
||||
d.EP5InBufferControl = 0
|
||||
d.EP5OutBufferControl = 0
|
||||
d.EP6InBufferControl = 0
|
||||
d.EP6OutBufferControl = 0
|
||||
d.EP7InBufferControl = 0
|
||||
d.EP7OutBufferControl = 0
|
||||
d.EP8InBufferControl = 0
|
||||
d.EP8OutBufferControl = 0
|
||||
d.EP9InBufferControl = 0
|
||||
d.EP9OutBufferControl = 0
|
||||
d.EP10InBufferControl = 0
|
||||
d.EP10OutBufferControl = 0
|
||||
d.EP11InBufferControl = 0
|
||||
d.EP11OutBufferControl = 0
|
||||
d.EP12InBufferControl = 0
|
||||
d.EP12OutBufferControl = 0
|
||||
d.EP13InBufferControl = 0
|
||||
d.EP13OutBufferControl = 0
|
||||
d.EP14InBufferControl = 0
|
||||
d.EP14OutBufferControl = 0
|
||||
d.EP15InBufferControl = 0
|
||||
d.EP15OutBufferControl = 0
|
||||
}
|
||||
@@ -3,5 +3,17 @@
|
||||
|
||||
package machine
|
||||
|
||||
type Serialer interface {
|
||||
WriteByte(c byte) error
|
||||
Write(data []byte) (n int, err error)
|
||||
Configure(config UARTConfig)
|
||||
Configured() bool
|
||||
Buffered() int
|
||||
ReadByte() (byte, error)
|
||||
}
|
||||
|
||||
//go:linkname NewUSBCDC machine/usb/cdc.New
|
||||
func NewUSBCDC() Serialer
|
||||
|
||||
// Serial is implemented via USB (USB-CDC).
|
||||
var Serial = USB
|
||||
var Serial = NewUSBCDC()
|
||||
|
||||
+183
-563
@@ -1,5 +1,5 @@
|
||||
//go:build sam || nrf52840
|
||||
// +build sam nrf52840
|
||||
//go:build sam || nrf52840 || rp2040
|
||||
// +build sam nrf52840 rp2040
|
||||
|
||||
package machine
|
||||
|
||||
@@ -8,415 +8,14 @@ import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const deviceDescriptorSize = 18
|
||||
type USBDevice struct {
|
||||
}
|
||||
|
||||
var (
|
||||
errUSBCDCBufferEmpty = errors.New("USB-CDC buffer empty")
|
||||
errUSBCDCWriteByteTimeout = errors.New("USB-CDC write byte timeout")
|
||||
errUSBCDCReadTimeout = errors.New("USB-CDC read timeout")
|
||||
errUSBCDCBytesRead = errors.New("USB-CDC invalid number of bytes read")
|
||||
USB = &USBDevice{}
|
||||
)
|
||||
|
||||
// DeviceDescriptor implements the USB standard device descriptor.
|
||||
//
|
||||
// Table 9-8. Standard Device Descriptor
|
||||
// bLength, bDescriptorType, bcdUSB, bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0,
|
||||
// idVendor, idProduct, bcdDevice, iManufacturer, iProduct, iSerialNumber, bNumConfigurations */
|
||||
//
|
||||
type DeviceDescriptor struct {
|
||||
bLength uint8 // 18
|
||||
bDescriptorType uint8 // 1 USB_DEVICE_DESCRIPTOR_TYPE
|
||||
bcdUSB uint16 // 0x200
|
||||
bDeviceClass uint8
|
||||
bDeviceSubClass uint8
|
||||
bDeviceProtocol uint8
|
||||
bMaxPacketSize0 uint8 // Packet 0
|
||||
idVendor uint16
|
||||
idProduct uint16
|
||||
bcdDevice uint16 // 0x100
|
||||
iManufacturer uint8
|
||||
iProduct uint8
|
||||
iSerialNumber uint8
|
||||
bNumConfigurations uint8
|
||||
}
|
||||
|
||||
// NewDeviceDescriptor returns a USB DeviceDescriptor.
|
||||
func NewDeviceDescriptor(class, subClass, proto, packetSize0 uint8, vid, pid, version uint16, im, ip, is, configs uint8) DeviceDescriptor {
|
||||
return DeviceDescriptor{deviceDescriptorSize, 1, 0x200, class, subClass, proto, packetSize0, vid, pid, version, im, ip, is, configs}
|
||||
}
|
||||
|
||||
// Bytes returns DeviceDescriptor data
|
||||
func (d DeviceDescriptor) Bytes() [deviceDescriptorSize]byte {
|
||||
var b [deviceDescriptorSize]byte
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bcdUSB)
|
||||
b[3] = byte(d.bcdUSB >> 8)
|
||||
b[4] = byte(d.bDeviceClass)
|
||||
b[5] = byte(d.bDeviceSubClass)
|
||||
b[6] = byte(d.bDeviceProtocol)
|
||||
b[7] = byte(d.bMaxPacketSize0)
|
||||
b[8] = byte(d.idVendor)
|
||||
b[9] = byte(d.idVendor >> 8)
|
||||
b[10] = byte(d.idProduct)
|
||||
b[11] = byte(d.idProduct >> 8)
|
||||
b[12] = byte(d.bcdDevice)
|
||||
b[13] = byte(d.bcdDevice >> 8)
|
||||
b[14] = byte(d.iManufacturer)
|
||||
b[15] = byte(d.iProduct)
|
||||
b[16] = byte(d.iSerialNumber)
|
||||
b[17] = byte(d.bNumConfigurations)
|
||||
return b
|
||||
}
|
||||
|
||||
const configDescriptorSize = 9
|
||||
|
||||
// ConfigDescriptor implements the standard USB configuration descriptor.
|
||||
//
|
||||
// Table 9-10. Standard Configuration Descriptor
|
||||
// bLength, bDescriptorType, wTotalLength, bNumInterfaces, bConfigurationValue, iConfiguration
|
||||
// bmAttributes, bMaxPower
|
||||
//
|
||||
type ConfigDescriptor struct {
|
||||
bLength uint8 // 9
|
||||
bDescriptorType uint8 // 2
|
||||
wTotalLength uint16 // total length
|
||||
bNumInterfaces uint8
|
||||
bConfigurationValue uint8
|
||||
iConfiguration uint8
|
||||
bmAttributes uint8
|
||||
bMaxPower uint8
|
||||
}
|
||||
|
||||
// NewConfigDescriptor returns a new USB ConfigDescriptor.
|
||||
func NewConfigDescriptor(totalLength uint16, interfaces uint8) ConfigDescriptor {
|
||||
return ConfigDescriptor{configDescriptorSize, 2, totalLength, interfaces, 1, 0, usb_CONFIG_BUS_POWERED | usb_CONFIG_REMOTE_WAKEUP, 50}
|
||||
}
|
||||
|
||||
// Bytes returns ConfigDescriptor data.
|
||||
func (d ConfigDescriptor) Bytes() [configDescriptorSize]byte {
|
||||
var b [configDescriptorSize]byte
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.wTotalLength)
|
||||
b[3] = byte(d.wTotalLength >> 8)
|
||||
b[4] = byte(d.bNumInterfaces)
|
||||
b[5] = byte(d.bConfigurationValue)
|
||||
b[6] = byte(d.iConfiguration)
|
||||
b[7] = byte(d.bmAttributes)
|
||||
b[8] = byte(d.bMaxPower)
|
||||
return b
|
||||
}
|
||||
|
||||
const interfaceDescriptorSize = 9
|
||||
|
||||
// InterfaceDescriptor implements the standard USB interface descriptor.
|
||||
//
|
||||
// Table 9-12. Standard Interface Descriptor
|
||||
// bLength, bDescriptorType, bInterfaceNumber, bAlternateSetting, bNumEndpoints, bInterfaceClass,
|
||||
// bInterfaceSubClass, bInterfaceProtocol, iInterface
|
||||
//
|
||||
type InterfaceDescriptor struct {
|
||||
bLength uint8 // 9
|
||||
bDescriptorType uint8 // 4
|
||||
bInterfaceNumber uint8
|
||||
bAlternateSetting uint8
|
||||
bNumEndpoints uint8
|
||||
bInterfaceClass uint8
|
||||
bInterfaceSubClass uint8
|
||||
bInterfaceProtocol uint8
|
||||
iInterface uint8
|
||||
}
|
||||
|
||||
// NewInterfaceDescriptor returns a new USB InterfaceDescriptor.
|
||||
func NewInterfaceDescriptor(n, numEndpoints, class, subClass, protocol uint8) InterfaceDescriptor {
|
||||
return InterfaceDescriptor{interfaceDescriptorSize, 4, n, 0, numEndpoints, class, subClass, protocol, 0}
|
||||
}
|
||||
|
||||
// Bytes returns InterfaceDescriptor data.
|
||||
func (d InterfaceDescriptor) Bytes() [interfaceDescriptorSize]byte {
|
||||
var b [interfaceDescriptorSize]byte
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bInterfaceNumber)
|
||||
b[3] = byte(d.bAlternateSetting)
|
||||
b[4] = byte(d.bNumEndpoints)
|
||||
b[5] = byte(d.bInterfaceClass)
|
||||
b[6] = byte(d.bInterfaceSubClass)
|
||||
b[7] = byte(d.bInterfaceProtocol)
|
||||
b[8] = byte(d.iInterface)
|
||||
return b
|
||||
}
|
||||
|
||||
const endpointDescriptorSize = 7
|
||||
|
||||
// EndpointDescriptor implements the standard USB endpoint descriptor.
|
||||
//
|
||||
// Table 9-13. Standard Endpoint Descriptor
|
||||
// bLength, bDescriptorType, bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval
|
||||
//
|
||||
type EndpointDescriptor struct {
|
||||
bLength uint8 // 7
|
||||
bDescriptorType uint8 // 5
|
||||
bEndpointAddress uint8
|
||||
bmAttributes uint8
|
||||
wMaxPacketSize uint16
|
||||
bInterval uint8
|
||||
}
|
||||
|
||||
// NewEndpointDescriptor returns a new USB EndpointDescriptor.
|
||||
func NewEndpointDescriptor(addr, attr uint8, packetSize uint16, interval uint8) EndpointDescriptor {
|
||||
return EndpointDescriptor{endpointDescriptorSize, 5, addr, attr, packetSize, interval}
|
||||
}
|
||||
|
||||
// Bytes returns EndpointDescriptor data.
|
||||
func (d EndpointDescriptor) Bytes() [endpointDescriptorSize]byte {
|
||||
var b [endpointDescriptorSize]byte
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bEndpointAddress)
|
||||
b[3] = byte(d.bmAttributes)
|
||||
b[4] = byte(d.wMaxPacketSize)
|
||||
b[5] = byte(d.wMaxPacketSize >> 8)
|
||||
b[6] = byte(d.bInterval)
|
||||
return b
|
||||
}
|
||||
|
||||
const iadDescriptorSize = 8
|
||||
|
||||
// IADDescriptor is an Interface Association Descriptor, which is used
|
||||
// to bind 2 interfaces together in CDC composite device.
|
||||
//
|
||||
// Standard Interface Association Descriptor:
|
||||
// bLength, bDescriptorType, bFirstInterface, bInterfaceCount, bFunctionClass, bFunctionSubClass,
|
||||
// bFunctionProtocol, iFunction
|
||||
//
|
||||
type IADDescriptor struct {
|
||||
bLength uint8 // 8
|
||||
bDescriptorType uint8 // 11
|
||||
bFirstInterface uint8
|
||||
bInterfaceCount uint8
|
||||
bFunctionClass uint8
|
||||
bFunctionSubClass uint8
|
||||
bFunctionProtocol uint8
|
||||
iFunction uint8
|
||||
}
|
||||
|
||||
// NewIADDescriptor returns a new USB IADDescriptor.
|
||||
func NewIADDescriptor(firstInterface, count, class, subClass, protocol uint8) IADDescriptor {
|
||||
return IADDescriptor{iadDescriptorSize, 11, firstInterface, count, class, subClass, protocol, 0}
|
||||
}
|
||||
|
||||
// Bytes returns IADDescriptor data.
|
||||
func (d IADDescriptor) Bytes() [iadDescriptorSize]byte {
|
||||
var b [iadDescriptorSize]byte
|
||||
b[0] = byte(d.bLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bFirstInterface)
|
||||
b[3] = byte(d.bInterfaceCount)
|
||||
b[4] = byte(d.bFunctionClass)
|
||||
b[5] = byte(d.bFunctionSubClass)
|
||||
b[6] = byte(d.bFunctionProtocol)
|
||||
b[7] = byte(d.iFunction)
|
||||
return b
|
||||
}
|
||||
|
||||
const cdcCSInterfaceDescriptorSize = 5
|
||||
|
||||
// CDCCSInterfaceDescriptor is a CDC CS interface descriptor.
|
||||
type CDCCSInterfaceDescriptor struct {
|
||||
len uint8 // 5
|
||||
dtype uint8 // 0x24
|
||||
subtype uint8
|
||||
d0 uint8
|
||||
d1 uint8
|
||||
}
|
||||
|
||||
// NewCDCCSInterfaceDescriptor returns a new USB CDCCSInterfaceDescriptor.
|
||||
func NewCDCCSInterfaceDescriptor(subtype, d0, d1 uint8) CDCCSInterfaceDescriptor {
|
||||
return CDCCSInterfaceDescriptor{cdcCSInterfaceDescriptorSize, 0x24, subtype, d0, d1}
|
||||
}
|
||||
|
||||
// Bytes returns CDCCSInterfaceDescriptor data.
|
||||
func (d CDCCSInterfaceDescriptor) Bytes() [cdcCSInterfaceDescriptorSize]byte {
|
||||
var b [cdcCSInterfaceDescriptorSize]byte
|
||||
b[0] = byte(d.len)
|
||||
b[1] = byte(d.dtype)
|
||||
b[2] = byte(d.subtype)
|
||||
b[3] = byte(d.d0)
|
||||
b[4] = byte(d.d1)
|
||||
return b
|
||||
}
|
||||
|
||||
const cmFunctionalDescriptorSize = 5
|
||||
|
||||
// CMFunctionalDescriptor is the functional descriptor general format.
|
||||
type CMFunctionalDescriptor struct {
|
||||
bFunctionLength uint8
|
||||
bDescriptorType uint8 // 0x24
|
||||
bDescriptorSubtype uint8 // 1
|
||||
bmCapabilities uint8
|
||||
bDataInterface uint8
|
||||
}
|
||||
|
||||
// NewCMFunctionalDescriptor returns a new USB CMFunctionalDescriptor.
|
||||
func NewCMFunctionalDescriptor(subtype, d0, d1 uint8) CMFunctionalDescriptor {
|
||||
return CMFunctionalDescriptor{5, 0x24, subtype, d0, d1}
|
||||
}
|
||||
|
||||
// Bytes returns the CMFunctionalDescriptor data.
|
||||
func (d CMFunctionalDescriptor) Bytes() [cmFunctionalDescriptorSize]byte {
|
||||
var b [cmFunctionalDescriptorSize]byte
|
||||
b[0] = byte(d.bFunctionLength)
|
||||
b[1] = byte(d.bDescriptorType)
|
||||
b[2] = byte(d.bDescriptorSubtype)
|
||||
b[3] = byte(d.bmCapabilities)
|
||||
b[4] = byte(d.bDescriptorSubtype)
|
||||
return b
|
||||
}
|
||||
|
||||
const acmFunctionalDescriptorSize = 4
|
||||
|
||||
// ACMFunctionalDescriptor is a Abstract Control Model (ACM) USB descriptor.
|
||||
type ACMFunctionalDescriptor struct {
|
||||
len uint8
|
||||
dtype uint8 // 0x24
|
||||
subtype uint8 // 1
|
||||
bmCapabilities uint8
|
||||
}
|
||||
|
||||
// NewACMFunctionalDescriptor returns a new USB ACMFunctionalDescriptor.
|
||||
func NewACMFunctionalDescriptor(subtype, d0 uint8) ACMFunctionalDescriptor {
|
||||
return ACMFunctionalDescriptor{4, 0x24, subtype, d0}
|
||||
}
|
||||
|
||||
// Bytes returns the ACMFunctionalDescriptor data.
|
||||
func (d ACMFunctionalDescriptor) Bytes() [acmFunctionalDescriptorSize]byte {
|
||||
var b [acmFunctionalDescriptorSize]byte
|
||||
b[0] = byte(d.len)
|
||||
b[1] = byte(d.dtype)
|
||||
b[2] = byte(d.subtype)
|
||||
b[3] = byte(d.bmCapabilities)
|
||||
return b
|
||||
}
|
||||
|
||||
// CDCDescriptor is the Communication Device Class (CDC) descriptor.
|
||||
type CDCDescriptor struct {
|
||||
// IAD
|
||||
iad IADDescriptor // Only needed on compound device
|
||||
|
||||
// Control
|
||||
cif InterfaceDescriptor
|
||||
header CDCCSInterfaceDescriptor
|
||||
|
||||
// CDC control
|
||||
controlManagement ACMFunctionalDescriptor // ACM
|
||||
functionalDescriptor CDCCSInterfaceDescriptor // CDC_UNION
|
||||
callManagement CMFunctionalDescriptor // Call Management
|
||||
cifin EndpointDescriptor
|
||||
|
||||
// CDC Data
|
||||
dif InterfaceDescriptor
|
||||
in EndpointDescriptor
|
||||
out EndpointDescriptor
|
||||
}
|
||||
|
||||
func NewCDCDescriptor(i IADDescriptor, c InterfaceDescriptor,
|
||||
h CDCCSInterfaceDescriptor,
|
||||
cm ACMFunctionalDescriptor,
|
||||
fd CDCCSInterfaceDescriptor,
|
||||
callm CMFunctionalDescriptor,
|
||||
ci EndpointDescriptor,
|
||||
di InterfaceDescriptor,
|
||||
outp EndpointDescriptor,
|
||||
inp EndpointDescriptor) CDCDescriptor {
|
||||
return CDCDescriptor{iad: i,
|
||||
cif: c,
|
||||
header: h,
|
||||
controlManagement: cm,
|
||||
functionalDescriptor: fd,
|
||||
callManagement: callm,
|
||||
cifin: ci,
|
||||
dif: di,
|
||||
in: inp,
|
||||
out: outp}
|
||||
}
|
||||
|
||||
const cdcSize = iadDescriptorSize +
|
||||
interfaceDescriptorSize +
|
||||
cdcCSInterfaceDescriptorSize +
|
||||
acmFunctionalDescriptorSize +
|
||||
cdcCSInterfaceDescriptorSize +
|
||||
cmFunctionalDescriptorSize +
|
||||
endpointDescriptorSize +
|
||||
interfaceDescriptorSize +
|
||||
endpointDescriptorSize +
|
||||
endpointDescriptorSize
|
||||
|
||||
// Bytes returns CDCDescriptor data.
|
||||
func (d CDCDescriptor) Bytes() [cdcSize]byte {
|
||||
var b [cdcSize]byte
|
||||
offset := 0
|
||||
|
||||
iad := d.iad.Bytes()
|
||||
copy(b[offset:], iad[:])
|
||||
offset += len(iad)
|
||||
|
||||
cif := d.cif.Bytes()
|
||||
copy(b[offset:], cif[:])
|
||||
offset += len(cif)
|
||||
|
||||
header := d.header.Bytes()
|
||||
copy(b[offset:], header[:])
|
||||
offset += len(header)
|
||||
|
||||
controlManagement := d.controlManagement.Bytes()
|
||||
copy(b[offset:], controlManagement[:])
|
||||
offset += len(controlManagement)
|
||||
|
||||
functionalDescriptor := d.functionalDescriptor.Bytes()
|
||||
copy(b[offset:], functionalDescriptor[:])
|
||||
offset += len(functionalDescriptor)
|
||||
|
||||
callManagement := d.callManagement.Bytes()
|
||||
copy(b[offset:], callManagement[:])
|
||||
offset += len(callManagement)
|
||||
|
||||
cifin := d.cifin.Bytes()
|
||||
copy(b[offset:], cifin[:])
|
||||
offset += len(cifin)
|
||||
|
||||
dif := d.dif.Bytes()
|
||||
copy(b[offset:], dif[:])
|
||||
offset += len(dif)
|
||||
|
||||
out := d.out.Bytes()
|
||||
copy(b[offset:], out[:])
|
||||
offset += len(out)
|
||||
|
||||
in := d.in.Bytes()
|
||||
copy(b[offset:], in[:])
|
||||
offset += len(in)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// MSCDescriptor is not used yet.
|
||||
type MSCDescriptor struct {
|
||||
msc InterfaceDescriptor
|
||||
in EndpointDescriptor
|
||||
out EndpointDescriptor
|
||||
}
|
||||
|
||||
const cdcLineInfoSize = 7
|
||||
|
||||
type cdcLineInfo struct {
|
||||
dwDTERate uint32
|
||||
bCharFormat uint8
|
||||
bParityType uint8
|
||||
bDataBits uint8
|
||||
lineState uint8
|
||||
}
|
||||
var usbDescriptor = descriptorCDC
|
||||
|
||||
// strToUTF16LEDescriptor converts a utf8 string into a string descriptor
|
||||
// note: the following code only converts ascii characters to UTF16LE. In order
|
||||
@@ -438,11 +37,34 @@ var (
|
||||
usb_STRING_LANGUAGE = [2]uint16{(3 << 8) | (2 + 2), 0x0409} // English
|
||||
)
|
||||
|
||||
const cdcLineInfoSize = 7
|
||||
|
||||
var (
|
||||
errUSBCDCBufferEmpty = errors.New("USB-CDC buffer empty")
|
||||
errUSBCDCWriteByteTimeout = errors.New("USB-CDC write byte timeout")
|
||||
errUSBCDCReadTimeout = errors.New("USB-CDC read timeout")
|
||||
errUSBCDCBytesRead = errors.New("USB-CDC invalid number of bytes read")
|
||||
)
|
||||
|
||||
var (
|
||||
usbEndpointDescriptors [8]usbDeviceDescriptor
|
||||
|
||||
udd_ep_in_cache_buffer [7][128 * 2]uint8
|
||||
udd_ep_out_cache_buffer [7][128 * 2]uint8
|
||||
|
||||
isEndpointHalt = false
|
||||
isRemoteWakeUpEnabled = false
|
||||
|
||||
usbConfiguration uint8
|
||||
usbSetInterface uint8
|
||||
)
|
||||
|
||||
const (
|
||||
usb_IMANUFACTURER = 1
|
||||
usb_IPRODUCT = 2
|
||||
usb_ISERIAL = 3
|
||||
|
||||
usb_ENDPOINT_TYPE_DISABLE = 0xFF
|
||||
usb_ENDPOINT_TYPE_CONTROL = 0x00
|
||||
usb_ENDPOINT_TYPE_ISOCHRONOUS = 0x01
|
||||
usb_ENDPOINT_TYPE_BULK = 0x02
|
||||
@@ -455,6 +77,8 @@ const (
|
||||
usb_ENDPOINT_DESCRIPTOR_TYPE = 5
|
||||
usb_DEVICE_QUALIFIER = 6
|
||||
usb_OTHER_SPEED_CONFIGURATION = 7
|
||||
usb_SET_REPORT_TYPE = 33
|
||||
usb_HID_REPORT_TYPE = 34
|
||||
|
||||
usbEndpointOut = 0x00
|
||||
usbEndpointIn = 0x80
|
||||
@@ -474,6 +98,9 @@ const (
|
||||
usb_GET_INTERFACE = 10
|
||||
usb_SET_INTERFACE = 11
|
||||
|
||||
// non standard requests
|
||||
usb_SET_IDLE = 10
|
||||
|
||||
usb_DEVICE_CLASS_COMMUNICATIONS = 0x02
|
||||
usb_DEVICE_CLASS_HUMAN_INTERFACE = 0x03
|
||||
usb_DEVICE_CLASS_STORAGE = 0x08
|
||||
@@ -488,9 +115,16 @@ const (
|
||||
usb_CDC_ACM_INTERFACE = 0 // CDC ACM
|
||||
usb_CDC_DATA_INTERFACE = 1 // CDC Data
|
||||
usb_CDC_FIRST_ENDPOINT = 1
|
||||
usb_CDC_ENDPOINT_ACM = 1
|
||||
usb_CDC_ENDPOINT_OUT = 2
|
||||
usb_CDC_ENDPOINT_IN = 3
|
||||
usb_HID_INTERFACE = 2 // HID
|
||||
|
||||
// Endpoint
|
||||
usb_CONTROL_ENDPOINT = 0
|
||||
usb_CDC_ENDPOINT_ACM = 1
|
||||
usb_CDC_ENDPOINT_OUT = 2
|
||||
usb_CDC_ENDPOINT_IN = 3
|
||||
usb_HID_ENDPOINT_IN = 4
|
||||
usb_MIDI_ENDPOINT_OUT = 5
|
||||
usb_MIDI_ENDPOINT_IN = 6
|
||||
|
||||
// bmRequestType
|
||||
usb_REQUEST_HOSTTODEVICE = 0x00
|
||||
@@ -511,27 +145,23 @@ const (
|
||||
usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
|
||||
usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE = (usb_REQUEST_HOSTTODEVICE | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
|
||||
usb_REQUEST_DEVICETOHOST_STANDARD_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_STANDARD | usb_REQUEST_INTERFACE)
|
||||
)
|
||||
|
||||
// CDC Class requests
|
||||
usb_CDC_SET_LINE_CODING = 0x20
|
||||
usb_CDC_GET_LINE_CODING = 0x21
|
||||
usb_CDC_SET_CONTROL_LINE_STATE = 0x22
|
||||
usb_CDC_SEND_BREAK = 0x23
|
||||
var (
|
||||
waitTxc [8]bool
|
||||
callbackUSBTx [8]func()
|
||||
callbackUSBRx [8]func([]byte)
|
||||
callbackUSBSetup [3]func(USBSetup) bool
|
||||
|
||||
usb_CDC_V1_10 = 0x0110
|
||||
usb_CDC_COMMUNICATION_INTERFACE_CLASS = 0x02
|
||||
|
||||
usb_CDC_CALL_MANAGEMENT = 0x01
|
||||
usb_CDC_ABSTRACT_CONTROL_MODEL = 0x02
|
||||
usb_CDC_HEADER = 0x00
|
||||
usb_CDC_ABSTRACT_CONTROL_MANAGEMENT = 0x02
|
||||
usb_CDC_UNION = 0x06
|
||||
usb_CDC_CS_INTERFACE = 0x24
|
||||
usb_CDC_CS_ENDPOINT = 0x25
|
||||
usb_CDC_DATA_INTERFACE_CLASS = 0x0A
|
||||
|
||||
usb_CDC_LINESTATE_DTR = 0x01
|
||||
usb_CDC_LINESTATE_RTS = 0x02
|
||||
endPoints = []uint32{
|
||||
usb_CONTROL_ENDPOINT: usb_ENDPOINT_TYPE_CONTROL,
|
||||
usb_CDC_ENDPOINT_ACM: (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
||||
usb_CDC_ENDPOINT_OUT: (usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
||||
usb_CDC_ENDPOINT_IN: (usb_ENDPOINT_TYPE_BULK | usbEndpointIn),
|
||||
usb_HID_ENDPOINT_IN: (usb_ENDPOINT_TYPE_DISABLE), // Interrupt In
|
||||
usb_MIDI_ENDPOINT_OUT: (usb_ENDPOINT_TYPE_DISABLE), // Bulk Out
|
||||
usb_MIDI_ENDPOINT_IN: (usb_ENDPOINT_TYPE_DISABLE), // Bulk In
|
||||
}
|
||||
)
|
||||
|
||||
// usbDeviceDescBank is the USB device endpoint descriptor.
|
||||
@@ -569,187 +199,177 @@ type usbDeviceDescriptor struct {
|
||||
// uint16_t wIndex;
|
||||
// uint16_t wLength;
|
||||
// } USBSetup;
|
||||
type usbSetup struct {
|
||||
bmRequestType uint8
|
||||
bRequest uint8
|
||||
wValueL uint8
|
||||
wValueH uint8
|
||||
wIndex uint16
|
||||
wLength uint16
|
||||
type USBSetup struct {
|
||||
BmRequestType uint8
|
||||
BRequest uint8
|
||||
WValueL uint8
|
||||
WValueH uint8
|
||||
WIndex uint16
|
||||
WLength uint16
|
||||
}
|
||||
|
||||
func newUSBSetup(data []byte) usbSetup {
|
||||
u := usbSetup{}
|
||||
u.bmRequestType = uint8(data[0])
|
||||
u.bRequest = uint8(data[1])
|
||||
u.wValueL = uint8(data[2])
|
||||
u.wValueH = uint8(data[3])
|
||||
u.wIndex = uint16(data[4]) | (uint16(data[5]) << 8)
|
||||
u.wLength = uint16(data[6]) | (uint16(data[7]) << 8)
|
||||
func newUSBSetup(data []byte) USBSetup {
|
||||
u := USBSetup{}
|
||||
u.BmRequestType = uint8(data[0])
|
||||
u.BRequest = uint8(data[1])
|
||||
u.WValueL = uint8(data[2])
|
||||
u.WValueH = uint8(data[3])
|
||||
u.WIndex = uint16(data[4]) | (uint16(data[5]) << 8)
|
||||
u.WLength = uint16(data[6]) | (uint16(data[7]) << 8)
|
||||
return u
|
||||
}
|
||||
|
||||
// USBCDC is the serial interface that works over the USB port.
|
||||
// To implement the USBCDC interface for a board, you must declare a concrete type as follows:
|
||||
//
|
||||
// type USBCDC struct {
|
||||
// Buffer *RingBuffer
|
||||
// }
|
||||
//
|
||||
// You can also add additional members to this struct depending on your implementation,
|
||||
// but the *RingBuffer is required.
|
||||
// When you are declaring the USBCDC for your board, make sure that you also declare the
|
||||
// RingBuffer using the NewRingBuffer() function:
|
||||
//
|
||||
// USBCDC{Buffer: NewRingBuffer()}
|
||||
//
|
||||
|
||||
// Read from the RX buffer.
|
||||
func (usbcdc *USBCDC) Read(data []byte) (n int, err error) {
|
||||
// check if RX buffer is empty
|
||||
size := usbcdc.Buffered()
|
||||
if size == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Make sure we do not read more from buffer than the data slice can hold.
|
||||
if len(data) < size {
|
||||
size = len(data)
|
||||
}
|
||||
|
||||
// only read number of bytes used from buffer
|
||||
for i := 0; i < size; i++ {
|
||||
v, _ := usbcdc.ReadByte()
|
||||
data[i] = v
|
||||
}
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// Write data to the USBCDC.
|
||||
func (usbcdc *USBCDC) Write(data []byte) (n int, err error) {
|
||||
for _, v := range data {
|
||||
usbcdc.WriteByte(v)
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// ReadByte reads a single byte from the RX buffer.
|
||||
// If there is no data in the buffer, returns an error.
|
||||
func (usbcdc *USBCDC) ReadByte() (byte, error) {
|
||||
// check if RX buffer is empty
|
||||
buf, ok := usbcdc.Buffer.Get()
|
||||
if !ok {
|
||||
return 0, errUSBCDCBufferEmpty
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes currently stored in the RX buffer.
|
||||
func (usbcdc *USBCDC) Buffered() int {
|
||||
return int(usbcdc.Buffer.Used())
|
||||
}
|
||||
|
||||
// Receive handles adding data to the UART's data buffer.
|
||||
// Usually called by the IRQ handler for a machine.
|
||||
func (usbcdc *USBCDC) Receive(data byte) {
|
||||
usbcdc.Buffer.Put(data)
|
||||
}
|
||||
|
||||
// sendDescriptor creates and sends the various USB descriptor types that
|
||||
// can be requested by the host.
|
||||
func sendDescriptor(setup usbSetup) {
|
||||
switch setup.wValueH {
|
||||
func sendDescriptor(setup USBSetup) {
|
||||
switch setup.WValueH {
|
||||
case usb_CONFIGURATION_DESCRIPTOR_TYPE:
|
||||
sendConfiguration(setup)
|
||||
sendUSBPacket(0, usbDescriptor.Configuration, setup.WLength)
|
||||
return
|
||||
case usb_DEVICE_DESCRIPTOR_TYPE:
|
||||
// composite descriptor
|
||||
dd := NewDeviceDescriptor(0xef, 0x02, 0x01, 64, usb_VID, usb_PID, 0x100, usb_IMANUFACTURER, usb_IPRODUCT, usb_ISERIAL, 1)
|
||||
l := deviceDescriptorSize
|
||||
if setup.wLength < deviceDescriptorSize {
|
||||
l = int(setup.wLength)
|
||||
}
|
||||
buf := dd.Bytes()
|
||||
sendUSBPacket(0, buf[:l])
|
||||
usbDescriptor.Configure(usb_VID, usb_PID)
|
||||
sendUSBPacket(0, usbDescriptor.Device, setup.WLength)
|
||||
return
|
||||
|
||||
case usb_STRING_DESCRIPTOR_TYPE:
|
||||
switch setup.wValueL {
|
||||
switch setup.WValueL {
|
||||
case 0:
|
||||
b := []byte{0x04, 0x03, 0x09, 0x04}
|
||||
sendUSBPacket(0, b)
|
||||
sendUSBPacket(0, b, setup.WLength)
|
||||
|
||||
case usb_IPRODUCT:
|
||||
b := make([]byte, (len(usb_STRING_PRODUCT)<<1)+2)
|
||||
strToUTF16LEDescriptor(usb_STRING_PRODUCT, b)
|
||||
sendUSBPacket(0, b)
|
||||
sendUSBPacket(0, b, setup.WLength)
|
||||
|
||||
case usb_IMANUFACTURER:
|
||||
b := make([]byte, (len(usb_STRING_MANUFACTURER)<<1)+2)
|
||||
strToUTF16LEDescriptor(usb_STRING_MANUFACTURER, b)
|
||||
sendUSBPacket(0, b)
|
||||
sendUSBPacket(0, b, setup.WLength)
|
||||
|
||||
case usb_ISERIAL:
|
||||
// TODO: allow returning a product serial number
|
||||
sendZlp()
|
||||
SendZlp()
|
||||
}
|
||||
return
|
||||
case usb_HID_REPORT_TYPE:
|
||||
if h, ok := usbDescriptor.HID[setup.WIndex]; ok {
|
||||
sendUSBPacket(0, h, setup.WLength)
|
||||
return
|
||||
}
|
||||
case usb_DEVICE_QUALIFIER:
|
||||
// skip
|
||||
default:
|
||||
}
|
||||
|
||||
// do not know how to handle this message, so return zero
|
||||
sendZlp()
|
||||
SendZlp()
|
||||
return
|
||||
}
|
||||
|
||||
// sendConfiguration creates and sends the configuration packet to the host.
|
||||
func sendConfiguration(setup usbSetup) {
|
||||
if setup.wLength == 9 {
|
||||
sz := uint16(configDescriptorSize + cdcSize)
|
||||
config := NewConfigDescriptor(sz, 2)
|
||||
configBuf := config.Bytes()
|
||||
sendUSBPacket(0, configBuf[:])
|
||||
} else {
|
||||
iad := NewIADDescriptor(0, 2, usb_CDC_COMMUNICATION_INTERFACE_CLASS, usb_CDC_ABSTRACT_CONTROL_MODEL, 0)
|
||||
func handleStandardSetup(setup USBSetup) bool {
|
||||
switch setup.BRequest {
|
||||
case usb_GET_STATUS:
|
||||
buf := []byte{0, 0}
|
||||
|
||||
cif := NewInterfaceDescriptor(usb_CDC_ACM_INTERFACE, 1, usb_CDC_COMMUNICATION_INTERFACE_CLASS, usb_CDC_ABSTRACT_CONTROL_MODEL, 0)
|
||||
if setup.BmRequestType != 0 { // endpoint
|
||||
// TODO: actually check if the endpoint in question is currently halted
|
||||
if isEndpointHalt {
|
||||
buf[0] = 1
|
||||
}
|
||||
}
|
||||
|
||||
header := NewCDCCSInterfaceDescriptor(usb_CDC_HEADER, usb_CDC_V1_10&0xFF, (usb_CDC_V1_10>>8)&0x0FF)
|
||||
sendUSBPacket(0, buf, setup.WLength)
|
||||
return true
|
||||
|
||||
controlManagement := NewACMFunctionalDescriptor(usb_CDC_ABSTRACT_CONTROL_MANAGEMENT, 6)
|
||||
case usb_CLEAR_FEATURE:
|
||||
if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = false
|
||||
} else if setup.WValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = false
|
||||
}
|
||||
SendZlp()
|
||||
return true
|
||||
|
||||
functionalDescriptor := NewCDCCSInterfaceDescriptor(usb_CDC_UNION, usb_CDC_ACM_INTERFACE, usb_CDC_DATA_INTERFACE)
|
||||
case usb_SET_FEATURE:
|
||||
if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = true
|
||||
} else if setup.WValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = true
|
||||
}
|
||||
SendZlp()
|
||||
return true
|
||||
|
||||
callManagement := NewCMFunctionalDescriptor(usb_CDC_CALL_MANAGEMENT, 1, 1)
|
||||
case usb_SET_ADDRESS:
|
||||
return handleUSBSetAddress(setup)
|
||||
|
||||
cifin := NewEndpointDescriptor((usb_CDC_ENDPOINT_ACM | usbEndpointIn), usb_ENDPOINT_TYPE_INTERRUPT, 0x10, 0x10)
|
||||
case usb_GET_DESCRIPTOR:
|
||||
sendDescriptor(setup)
|
||||
return true
|
||||
|
||||
dif := NewInterfaceDescriptor(usb_CDC_DATA_INTERFACE, 2, usb_CDC_DATA_INTERFACE_CLASS, 0, 0)
|
||||
case usb_SET_DESCRIPTOR:
|
||||
return false
|
||||
|
||||
out := NewEndpointDescriptor((usb_CDC_ENDPOINT_OUT | usbEndpointOut), usb_ENDPOINT_TYPE_BULK, usbEndpointPacketSize, 0)
|
||||
case usb_GET_CONFIGURATION:
|
||||
buff := []byte{usbConfiguration}
|
||||
sendUSBPacket(0, buff, setup.WLength)
|
||||
return true
|
||||
|
||||
in := NewEndpointDescriptor((usb_CDC_ENDPOINT_IN | usbEndpointIn), usb_ENDPOINT_TYPE_BULK, usbEndpointPacketSize, 0)
|
||||
case usb_SET_CONFIGURATION:
|
||||
if setup.BmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE {
|
||||
for i := 1; i < len(endPoints); i++ {
|
||||
initEndpoint(uint32(i), endPoints[i])
|
||||
}
|
||||
|
||||
cdc := NewCDCDescriptor(iad,
|
||||
cif,
|
||||
header,
|
||||
controlManagement,
|
||||
functionalDescriptor,
|
||||
callManagement,
|
||||
cifin,
|
||||
dif,
|
||||
out,
|
||||
in)
|
||||
usbConfiguration = setup.WValueL
|
||||
|
||||
sz := uint16(configDescriptorSize + cdcSize)
|
||||
config := NewConfigDescriptor(sz, 2)
|
||||
SendZlp()
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
configBuf := config.Bytes()
|
||||
cdcBuf := cdc.Bytes()
|
||||
var buf [configDescriptorSize + cdcSize]byte
|
||||
copy(buf[0:], configBuf[:])
|
||||
copy(buf[configDescriptorSize:], cdcBuf[:])
|
||||
case usb_GET_INTERFACE:
|
||||
buff := []byte{usbSetInterface}
|
||||
sendUSBPacket(0, buff, setup.WLength)
|
||||
return true
|
||||
|
||||
sendUSBPacket(0, buf[:])
|
||||
case usb_SET_INTERFACE:
|
||||
usbSetInterface = setup.WValueL
|
||||
|
||||
SendZlp()
|
||||
return true
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func EnableCDC(callback func(), callbackRx func([]byte), callbackSetup func(USBSetup) bool) {
|
||||
//usbDescriptor = descriptorCDC
|
||||
endPoints[usb_CDC_ENDPOINT_ACM] = (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn)
|
||||
endPoints[usb_CDC_ENDPOINT_OUT] = (usb_ENDPOINT_TYPE_BULK | usbEndpointOut)
|
||||
endPoints[usb_CDC_ENDPOINT_IN] = (usb_ENDPOINT_TYPE_BULK | usbEndpointIn)
|
||||
callbackUSBRx[usb_CDC_ENDPOINT_OUT] = callbackRx
|
||||
callbackUSBTx[usb_CDC_ENDPOINT_IN] = callback
|
||||
callbackUSBSetup[usb_CDC_ACM_INTERFACE] = callbackSetup // 0x02 (Communications and CDC Control)
|
||||
callbackUSBSetup[usb_CDC_DATA_INTERFACE] = nil // 0x0A (CDC-Data)
|
||||
}
|
||||
|
||||
// EnableHID enables HID. This function must be executed from the init().
|
||||
func EnableHID(callback func(), callbackRx func([]byte), callbackSetup func(USBSetup) bool) {
|
||||
usbDescriptor = descriptorCDCHID
|
||||
endPoints[usb_HID_ENDPOINT_IN] = (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn)
|
||||
callbackUSBTx[usb_HID_ENDPOINT_IN] = callback
|
||||
callbackUSBSetup[usb_HID_INTERFACE] = callbackSetup // 0x03 (HID - Human Interface Device)
|
||||
}
|
||||
|
||||
// EnableMIDI enables MIDI. This function must be executed from the init().
|
||||
func EnableMIDI(callback func(), callbackRx func([]byte), callbackSetup func(USBSetup) bool) {
|
||||
usbDescriptor = descriptorCDCMIDI
|
||||
endPoints[usb_MIDI_ENDPOINT_OUT] = (usb_ENDPOINT_TYPE_BULK | usbEndpointOut)
|
||||
endPoints[usb_MIDI_ENDPOINT_IN] = (usb_ENDPOINT_TYPE_BULK | usbEndpointIn)
|
||||
callbackUSBRx[usb_MIDI_ENDPOINT_OUT] = callbackRx
|
||||
callbackUSBTx[usb_MIDI_ENDPOINT_IN] = callback
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package cdc
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const bufferSize = 128
|
||||
|
||||
// RingBuffer is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
type RingBuffer struct {
|
||||
rxbuffer [bufferSize]volatile.Register8
|
||||
head volatile.Register8
|
||||
tail volatile.Register8
|
||||
}
|
||||
|
||||
// NewRingBuffer returns a new ring buffer.
|
||||
func NewRingBuffer() *RingBuffer {
|
||||
return &RingBuffer{}
|
||||
}
|
||||
|
||||
// Used returns how many bytes in buffer have been used.
|
||||
func (rb *RingBuffer) Used() uint8 {
|
||||
return uint8(rb.head.Get() - rb.tail.Get())
|
||||
}
|
||||
|
||||
// Put stores a byte in the buffer. If the buffer is already
|
||||
// full, the method will return false.
|
||||
func (rb *RingBuffer) Put(val byte) bool {
|
||||
if rb.Used() != bufferSize {
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
rb.rxbuffer[rb.head.Get()%bufferSize].Set(val)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get returns a byte from the buffer. If the buffer is empty,
|
||||
// the method will return a false as the second value.
|
||||
func (rb *RingBuffer) Get() (byte, bool) {
|
||||
if rb.Used() != 0 {
|
||||
rb.tail.Set(rb.tail.Get() + 1)
|
||||
return rb.rxbuffer[rb.tail.Get()%bufferSize].Get(), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Clear resets the head and tail pointer to zero.
|
||||
func (rb *RingBuffer) Clear() {
|
||||
rb.head.Set(0)
|
||||
rb.tail.Set(0)
|
||||
}
|
||||
|
||||
const bufferSize2 = 8
|
||||
|
||||
// RingBuffer2 is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
type RingBuffer2 struct {
|
||||
rxbuffer [bufferSize2]struct {
|
||||
buf [64]byte
|
||||
size int
|
||||
}
|
||||
head volatile.Register8
|
||||
tail volatile.Register8
|
||||
}
|
||||
|
||||
// NewRingBuffer returns a new ring buffer.
|
||||
func NewRingBuffer2() *RingBuffer2 {
|
||||
return &RingBuffer2{}
|
||||
}
|
||||
|
||||
// Used returns how many bytes in buffer have been used.
|
||||
func (rb *RingBuffer2) Used() uint8 {
|
||||
return uint8(rb.head.Get() - rb.tail.Get())
|
||||
}
|
||||
|
||||
// Put stores a byte in the buffer. If the buffer is already
|
||||
// full, the method will return false.
|
||||
func (rb *RingBuffer2) Put(val []byte) bool {
|
||||
if rb.Used() == bufferSize2 {
|
||||
return false
|
||||
}
|
||||
|
||||
if rb.Used() == 0 {
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
rb.rxbuffer[rb.head.Get()%bufferSize2].size = 0
|
||||
}
|
||||
buf := &rb.rxbuffer[rb.head.Get()%bufferSize2]
|
||||
|
||||
for i := 0; i < len(val); i++ {
|
||||
if buf.size == 64 {
|
||||
// next
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
buf = &rb.rxbuffer[rb.head.Get()%bufferSize2]
|
||||
rb.rxbuffer[rb.head.Get()%bufferSize2].size = 0
|
||||
}
|
||||
buf.buf[buf.size] = val[i]
|
||||
buf.size++
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Get returns a byte from the buffer. If the buffer is empty,
|
||||
// the method will return a false as the second value.
|
||||
func (rb *RingBuffer2) Get() ([]byte, bool) {
|
||||
if rb.Used() != 0 {
|
||||
rb.tail.Set(rb.tail.Get() + 1)
|
||||
size := rb.rxbuffer[rb.tail.Get()%bufferSize2].size
|
||||
return rb.rxbuffer[rb.tail.Get()%bufferSize2].buf[:size], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Clear resets the head and tail pointer to zero.
|
||||
func (rb *RingBuffer2) Clear() {
|
||||
rb.head.Set(0)
|
||||
rb.tail.Set(0)
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package cdc
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
const (
|
||||
cdcEndpointACM = 1
|
||||
cdcEndpointOut = 2
|
||||
cdcEndpointIn = 3
|
||||
)
|
||||
|
||||
var CDC *cdc
|
||||
|
||||
type cdc struct {
|
||||
buf *RingBuffer
|
||||
callbackFuncRx func([]byte)
|
||||
}
|
||||
|
||||
// New returns hid-mouse.
|
||||
func New() *USBCDC {
|
||||
USB = &USBCDC{
|
||||
Buffer: NewRingBuffer(),
|
||||
Buffer2: NewRingBuffer2(),
|
||||
}
|
||||
return USB
|
||||
}
|
||||
|
||||
func newCDC() *cdc {
|
||||
m := &cdc{
|
||||
buf: NewRingBuffer(),
|
||||
}
|
||||
//machine.EnableCDC(m.Callback, m.CallbackRx)
|
||||
return m
|
||||
}
|
||||
|
||||
func (c *cdc) SetCallback(callbackRx func([]byte)) {
|
||||
c.callbackFuncRx = callbackRx
|
||||
}
|
||||
|
||||
func (c *cdc) Write(b []byte) (n int, err error) {
|
||||
i := 0
|
||||
for i = 0; i < len(b); i++ {
|
||||
c.buf.Put(b[i])
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (c *cdc) sendUSBPacket(b []byte) {
|
||||
machine.SendUSBInPacket(cdcEndpointIn, b)
|
||||
}
|
||||
|
||||
// from BulkIn
|
||||
func (c *cdc) Callback() {
|
||||
if b, ok := c.buf.Get(); ok {
|
||||
c.sendUSBPacket([]byte{b})
|
||||
}
|
||||
}
|
||||
|
||||
// from BulkOut
|
||||
func (c *cdc) CallbackRx(b []byte) {
|
||||
if c.callbackFuncRx != nil {
|
||||
c.callbackFuncRx(b)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// bmRequestType
|
||||
usb_REQUEST_HOSTTODEVICE = 0x00
|
||||
usb_REQUEST_DEVICETOHOST = 0x80
|
||||
usb_REQUEST_DIRECTION = 0x80
|
||||
|
||||
usb_REQUEST_STANDARD = 0x00
|
||||
usb_REQUEST_CLASS = 0x20
|
||||
usb_REQUEST_VENDOR = 0x40
|
||||
usb_REQUEST_TYPE = 0x60
|
||||
|
||||
usb_REQUEST_DEVICE = 0x00
|
||||
usb_REQUEST_INTERFACE = 0x01
|
||||
usb_REQUEST_ENDPOINT = 0x02
|
||||
usb_REQUEST_OTHER = 0x03
|
||||
usb_REQUEST_RECIPIENT = 0x1F
|
||||
|
||||
usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
|
||||
usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE = (usb_REQUEST_HOSTTODEVICE | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
|
||||
usb_REQUEST_DEVICETOHOST_STANDARD_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_STANDARD | usb_REQUEST_INTERFACE)
|
||||
|
||||
// CDC Class requests
|
||||
usb_CDC_SET_LINE_CODING = 0x20
|
||||
usb_CDC_GET_LINE_CODING = 0x21
|
||||
usb_CDC_SET_CONTROL_LINE_STATE = 0x22
|
||||
usb_CDC_SEND_BREAK = 0x23
|
||||
|
||||
usb_CDC_V1_10 = 0x0110
|
||||
usb_CDC_COMMUNICATION_INTERFACE_CLASS = 0x02
|
||||
|
||||
usb_CDC_CALL_MANAGEMENT = 0x01
|
||||
usb_CDC_ABSTRACT_CONTROL_MODEL = 0x02
|
||||
usb_CDC_HEADER = 0x00
|
||||
usb_CDC_ABSTRACT_CONTROL_MANAGEMENT = 0x02
|
||||
usb_CDC_UNION = 0x06
|
||||
usb_CDC_CS_INTERFACE = 0x24
|
||||
usb_CDC_CS_ENDPOINT = 0x25
|
||||
usb_CDC_DATA_INTERFACE_CLASS = 0x0A
|
||||
|
||||
usb_CDC_LINESTATE_DTR = 0x01
|
||||
usb_CDC_LINESTATE_RTS = 0x02
|
||||
)
|
||||
|
||||
func (c *cdc) handleSetup(setup machine.USBSetup) bool {
|
||||
if setup.BmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.BRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
//c.sendUSBPacket(0, b[:], setup.WLength)
|
||||
c.sendUSBPacket(b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.BmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.BRequest == usb_CDC_SET_LINE_CODING {
|
||||
b, err := machine.ReceiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.WValueL
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SET_LINE_CODING || setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
// auto-reset into the bootloader
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
machine.ResetProcessor()
|
||||
} else {
|
||||
// TODO: cancel any reset
|
||||
}
|
||||
sendZlp()
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SEND_BREAK {
|
||||
// TODO: something with this value?
|
||||
// breakValue = ((uint16_t)setup.WValueH << 8) | setup.WValueL;
|
||||
// return false;
|
||||
sendZlp()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package cdc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
var (
|
||||
errUSBCDCBufferEmpty = errors.New("USB-CDC buffer empty")
|
||||
errUSBCDCWriteByteTimeout = errors.New("USB-CDC write byte timeout")
|
||||
errUSBCDCReadTimeout = errors.New("USB-CDC read timeout")
|
||||
errUSBCDCBytesRead = errors.New("USB-CDC invalid number of bytes read")
|
||||
)
|
||||
|
||||
const cdcLineInfoSize = 7
|
||||
|
||||
type cdcLineInfo struct {
|
||||
dwDTERate uint32
|
||||
bCharFormat uint8
|
||||
bParityType uint8
|
||||
bDataBits uint8
|
||||
lineState uint8
|
||||
}
|
||||
|
||||
// USBCDC is the serial interface that works over the USB port.
|
||||
// To implement the USBCDC interface for a board, you must declare a concrete type as follows:
|
||||
//
|
||||
// type USBCDC struct {
|
||||
// Buffer *RingBuffer
|
||||
// }
|
||||
//
|
||||
// You can also add additional members to this struct depending on your implementation,
|
||||
// but the *RingBuffer is required.
|
||||
// When you are declaring the USBCDC for your board, make sure that you also declare the
|
||||
// RingBuffer using the NewRingBuffer() function:
|
||||
//
|
||||
// USBCDC{Buffer: NewRingBuffer()}
|
||||
//
|
||||
|
||||
// Read from the RX buffer.
|
||||
func (usbcdc *USBCDC) Read(data []byte) (n int, err error) {
|
||||
// check if RX buffer is empty
|
||||
size := usbcdc.Buffered()
|
||||
if size == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Make sure we do not read more from buffer than the data slice can hold.
|
||||
if len(data) < size {
|
||||
size = len(data)
|
||||
}
|
||||
|
||||
// only read number of bytes used from buffer
|
||||
for i := 0; i < size; i++ {
|
||||
v, _ := usbcdc.ReadByte()
|
||||
data[i] = v
|
||||
}
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// ReadByte reads a single byte from the RX buffer.
|
||||
// If there is no data in the buffer, returns an error.
|
||||
func (usbcdc *USBCDC) ReadByte() (byte, error) {
|
||||
// check if RX buffer is empty
|
||||
buf, ok := usbcdc.Buffer.Get()
|
||||
if !ok {
|
||||
return 0, errUSBCDCBufferEmpty
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes currently stored in the RX buffer.
|
||||
func (usbcdc *USBCDC) Buffered() int {
|
||||
return int(usbcdc.Buffer.Used())
|
||||
}
|
||||
|
||||
// Receive handles adding data to the UART's data buffer.
|
||||
// Usually called by the IRQ handler for a machine.
|
||||
func (usbcdc *USBCDC) Receive(data byte) {
|
||||
usbcdc.Buffer.Put(data)
|
||||
}
|
||||
|
||||
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
|
||||
type USBCDC struct {
|
||||
Buffer *RingBuffer
|
||||
Buffer2 *RingBuffer2
|
||||
TxIdx volatile.Register8
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
configured bool
|
||||
waitTxc bool
|
||||
}
|
||||
|
||||
func (x *USBCDC) Debug() int {
|
||||
return 3
|
||||
}
|
||||
|
||||
var (
|
||||
// USB is a USB CDC interface.
|
||||
USB *USBCDC
|
||||
|
||||
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
|
||||
)
|
||||
|
||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
||||
func (usbcdc *USBCDC) Configure(config machine.UARTConfig) {
|
||||
}
|
||||
|
||||
// Flush flushes buffered data.
|
||||
func (usbcdc *USBCDC) Flush() {
|
||||
mask := interrupt.Disable()
|
||||
if b, ok := usbcdc.Buffer2.Get(); ok {
|
||||
machine.SendUSBInPacket(cdcEndpointIn, b)
|
||||
} else {
|
||||
usbcdc.waitTxc = false
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
|
||||
// Write data to the USBCDC.
|
||||
func (usbcdc *USBCDC) Write(data []byte) (n int, err error) {
|
||||
if usbLineInfo.lineState > 0 {
|
||||
mask := interrupt.Disable()
|
||||
usbcdc.Buffer2.Put(data)
|
||||
if !usbcdc.waitTxc {
|
||||
usbcdc.waitTxc = true
|
||||
usbcdc.Flush()
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the USB CDC interface.
|
||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||
usbcdc.Write([]byte{c})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) DTR() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) RTS() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
||||
}
|
||||
|
||||
// Configured returns whether usbcdc is configured or not.
|
||||
func (usbcdc *USBCDC) Configured() bool {
|
||||
return usbcdc.configured
|
||||
}
|
||||
|
||||
func cdcCallbackRx(b []byte) {
|
||||
for i := range b {
|
||||
USB.Receive(b[i])
|
||||
}
|
||||
}
|
||||
|
||||
func cdcSetup(setup machine.USBSetup) bool {
|
||||
if setup.BmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.BRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
//machine.SendUSBPacket(0, b[:], setup.WLength)
|
||||
machine.SendUSBInPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.BmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.BRequest == usb_CDC_SET_LINE_CODING {
|
||||
b, err := machine.ReceiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.WValueL
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SET_LINE_CODING || setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
// auto-reset into the bootloader
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
machine.ResetProcessor()
|
||||
} else {
|
||||
// TODO: cancel any reset
|
||||
}
|
||||
sendZlp()
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SEND_BREAK {
|
||||
// TODO: something with this value?
|
||||
// breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
|
||||
// return false;
|
||||
sendZlp()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func EnableUSBCDC() {
|
||||
machine.Serial = USB
|
||||
machine.EnableCDC(USB.Flush, cdcCallbackRx, cdcSetup)
|
||||
}
|
||||
|
||||
func sendZlp() {
|
||||
machine.SendZlp()
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package hid
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const bufferSize = 128
|
||||
|
||||
// RingBuffer is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
type RingBuffer struct {
|
||||
rxbuffer [bufferSize][9]byte
|
||||
head volatile.Register8
|
||||
tail volatile.Register8
|
||||
}
|
||||
|
||||
// NewRingBuffer returns a new ring buffer.
|
||||
func NewRingBuffer() *RingBuffer {
|
||||
return &RingBuffer{}
|
||||
}
|
||||
|
||||
// Used returns how many bytes in buffer have been used.
|
||||
func (rb *RingBuffer) Used() uint8 {
|
||||
return uint8(rb.head.Get() - rb.tail.Get())
|
||||
}
|
||||
|
||||
// Put stores a byte in the buffer. If the buffer is already
|
||||
// full, the method will return false.
|
||||
func (rb *RingBuffer) Put(val []byte) bool {
|
||||
if rb.Used() != bufferSize {
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
copy(rb.rxbuffer[rb.head.Get()%bufferSize][:], val)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get returns a byte from the buffer. If the buffer is empty,
|
||||
// the method will return a false as the second value.
|
||||
func (rb *RingBuffer) Get() ([]byte, bool) {
|
||||
if rb.Used() != 0 {
|
||||
rb.tail.Set(rb.tail.Get() + 1)
|
||||
return rb.rxbuffer[rb.tail.Get()%bufferSize][:], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Clear resets the head and tail pointer to zero.
|
||||
func (rb *RingBuffer) Clear() {
|
||||
rb.head.Set(0)
|
||||
rb.tail.Set(0)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package hid
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
)
|
||||
|
||||
// from usb-hid.go
|
||||
var (
|
||||
ErrHIDInvalidPort = errors.New("invalid USB port")
|
||||
ErrHIDInvalidCore = errors.New("invalid USB core")
|
||||
ErrHIDReportTransfer = errors.New("failed to transfer HID report")
|
||||
)
|
||||
|
||||
const (
|
||||
hidEndpoint = 4
|
||||
|
||||
usb_SET_REPORT_TYPE = 33
|
||||
usb_SET_IDLE = 10
|
||||
)
|
||||
|
||||
type hidDevicer interface {
|
||||
Callback() bool
|
||||
}
|
||||
|
||||
var devices [5]hidDevicer
|
||||
var size int
|
||||
|
||||
// SetCallbackHandler sets the callback. Only the first time it is called, it
|
||||
// calls machine.EnableHID for USB configuration
|
||||
func SetCallbackHandler(d hidDevicer) {
|
||||
if size == 0 {
|
||||
machine.EnableHID(callback, nil, nil)
|
||||
}
|
||||
|
||||
devices[size] = d
|
||||
size++
|
||||
}
|
||||
|
||||
func callback() {
|
||||
for _, d := range devices {
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
if done := d.Callback(); done {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func callbackSetup(setup machine.USBSetup) bool {
|
||||
ok := false
|
||||
if setup.BmRequestType == usb_SET_REPORT_TYPE && setup.BRequest == usb_SET_IDLE {
|
||||
machine.SendZlp()
|
||||
ok = true
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// SendUSBPacket sends a HIDPacket.
|
||||
func SendUSBPacket(b []byte) {
|
||||
machine.SendUSBInPacket(hidEndpoint, b)
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
package keyboard
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine/usb/hid"
|
||||
)
|
||||
|
||||
// from usb-hid-keyboard.go
|
||||
var (
|
||||
ErrInvalidCodepoint = errors.New("invalid Unicode codepoint")
|
||||
ErrInvalidKeycode = errors.New("invalid keyboard keycode")
|
||||
ErrInvalidUTF8 = errors.New("invalid UTF-8 encoding")
|
||||
ErrKeypressMaximum = errors.New("maximum keypresses exceeded")
|
||||
)
|
||||
|
||||
var Keyboard *keyboard
|
||||
|
||||
// Keyboard represents a USB HID keyboard device with support for international
|
||||
// layouts and various control, system, multimedia, and consumer keycodes.
|
||||
//
|
||||
// Keyboard implements the io.Writer interface that translates UTF-8 encoded
|
||||
// byte strings into sequences of keypress events.
|
||||
type keyboard struct {
|
||||
// led holds the current state of all keyboard LEDs:
|
||||
// 1=NumLock 2=CapsLock 4=ScrollLock 8=Compose 16=Kana
|
||||
led uint8
|
||||
|
||||
// mod holds the current state of all keyboard modifier keys:
|
||||
// 1=LeftCtrl 2=LeftShift 4=LeftAlt 8=LeftGUI
|
||||
// 16=RightCtrl 32=RightShift 64=RightAlt 128=RightGUI
|
||||
mod uint8
|
||||
|
||||
// key holds a list of all keyboard keys currently pressed.
|
||||
key [hidKeyboardKeyCount]uint8
|
||||
con [hidKeyboardConCount]uint16
|
||||
sys [hidKeyboardSysCount]uint8
|
||||
|
||||
// decode holds the current state of the UTF-8 decoder.
|
||||
decode decodeState
|
||||
|
||||
// wideChar holds high bits for the UTF-8 decoder.
|
||||
wideChar uint16
|
||||
|
||||
buf *hid.RingBuffer
|
||||
waitTxc bool
|
||||
}
|
||||
|
||||
// decodeState represents a state in the UTF-8 decode state machine.
|
||||
type decodeState uint8
|
||||
|
||||
// Constant enumerated values of type decodeState.
|
||||
const (
|
||||
decodeReset decodeState = iota
|
||||
decodeByte1
|
||||
decodeByte2
|
||||
decodeByte3
|
||||
)
|
||||
|
||||
func init() {
|
||||
if Keyboard == nil {
|
||||
Keyboard = newKeyboard()
|
||||
hid.SetCallbackHandler(Keyboard)
|
||||
}
|
||||
}
|
||||
|
||||
// New returns hid-keybord.
|
||||
func New() *keyboard {
|
||||
return Keyboard
|
||||
}
|
||||
|
||||
func newKeyboard() *keyboard {
|
||||
return &keyboard{
|
||||
buf: hid.NewRingBuffer(),
|
||||
}
|
||||
}
|
||||
|
||||
func (kb *keyboard) Callback() bool {
|
||||
kb.waitTxc = false
|
||||
if b, ok := kb.buf.Get(); ok {
|
||||
kb.waitTxc = true
|
||||
hid.SendUSBPacket(b)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (kb *keyboard) tx(b []byte) {
|
||||
if kb.waitTxc {
|
||||
kb.buf.Put(b)
|
||||
} else {
|
||||
kb.waitTxc = true
|
||||
hid.SendUSBPacket(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (kb *keyboard) ready() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Write transmits press-and-release key sequences for each Keycode translated
|
||||
// from the given UTF-8 byte string. Write implements the io.Writer interface
|
||||
// and conforms to all documented conventions for arguments and return values.
|
||||
func (kb *keyboard) Write(b []byte) (n int, err error) {
|
||||
for _, c := range b {
|
||||
if err = kb.WriteByte(c); nil != err {
|
||||
break
|
||||
}
|
||||
n += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// WriteByte processes a single byte from a UTF-8 byte string. This method is a
|
||||
// stateful method with respect to the receiver Keyboard, meaning that its exact
|
||||
// behavior will depend on the current state of its UTF-8 decode state machine:
|
||||
//
|
||||
// (a) If the given byte is a valid ASCII encoding (0-127), then a keypress
|
||||
// sequence is immediately transmitted for the respective Keycode.
|
||||
//
|
||||
// (b) If the given byte represents the final byte in a multi-byte codepoint,
|
||||
// then a keypress sequence is immediately transmitted by translating the
|
||||
// multi-byte codepoint to its respective Keycode.
|
||||
//
|
||||
// (c) If the given byte appears to represent high bits for a multi-byte
|
||||
// codepoint, then the bits are copied to the receiver's internal state
|
||||
// machine buffer for use by a subsequent call to WriteByte() (or Write())
|
||||
// that completes the codepoint.
|
||||
//
|
||||
// (d) If the given byte is out of range, or contains illegal bits for the
|
||||
// current state of the UTF-8 decoder, then the UTF-8 decode state machine
|
||||
// is reset to its initial state.
|
||||
//
|
||||
// In cases (c) and (d), a keypress sequence is not generated and no data is
|
||||
// transmitted. In case (c), additional bytes must be received via WriteByte()
|
||||
// (or Write()) to complete or discard the current codepoint.
|
||||
func (kb *keyboard) WriteByte(b byte) error {
|
||||
switch {
|
||||
case b < 0x80:
|
||||
// 1-byte encoding (0x00-0x7F)
|
||||
kb.decode = decodeByte1
|
||||
return kb.write(uint16(b))
|
||||
|
||||
case b < 0xC0:
|
||||
// 2nd, 3rd, or 4th byte (0x80-0xBF)
|
||||
b = Keycode(b).key()
|
||||
switch kb.decode {
|
||||
case decodeByte2:
|
||||
kb.decode = decodeByte1
|
||||
return kb.write(kb.wideChar | uint16(b))
|
||||
case decodeByte3:
|
||||
kb.decode = decodeByte2
|
||||
kb.wideChar |= uint16(b) << 6
|
||||
}
|
||||
|
||||
case b < 0xE0:
|
||||
// 2-byte encoding (0xC2-0xDF), or illegal byte 2 (0xC0-0xC1)
|
||||
kb.decode = decodeByte2
|
||||
kb.wideChar = uint16(b&0x1F) << 6
|
||||
|
||||
case b < 0xF0:
|
||||
// 3-byte encoding (0xE0-0xEF)
|
||||
kb.decode = decodeByte3
|
||||
kb.wideChar = uint16(b&0x0F) << 12
|
||||
|
||||
default:
|
||||
// 4-byte encoding unsupported (0xF0-0xF4), or illegal byte 4 (0xF5-0xFF)
|
||||
kb.decode = decodeReset
|
||||
return ErrInvalidUTF8
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *keyboard) write(p uint16) error {
|
||||
c := keycode(p)
|
||||
if 0 == c {
|
||||
return ErrInvalidCodepoint
|
||||
}
|
||||
if d := deadkey(c); 0 != d {
|
||||
if err := kb.writeKeycode(d); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return kb.writeKeycode(c)
|
||||
}
|
||||
|
||||
func (kb *keyboard) writeKeycode(c Keycode) error {
|
||||
var b [9]byte
|
||||
b[0] = 0x02
|
||||
b[1] = c.mod()
|
||||
b[2] = 0
|
||||
b[3] = c.key()
|
||||
b[4] = 0
|
||||
b[5] = 0
|
||||
b[6] = 0
|
||||
b[7] = 0
|
||||
b[8] = 0
|
||||
if !kb.sendKey(false, b[:]) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
|
||||
b[1] = 0
|
||||
b[3] = 0
|
||||
if !kb.sendKey(false, b[:]) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Press transmits a press-and-release sequence for the given Keycode, which
|
||||
// simulates a discrete keypress event.
|
||||
//
|
||||
// The following values of Keycode are supported:
|
||||
//
|
||||
// 0x0020 - 0x007F ASCII (U+0020 to U+007F) [USES LAYOUT]
|
||||
// 0x0080 - 0xC1FF Unicode (U+0080 to U+C1FF) [USES LAYOUT]
|
||||
// 0xC200 - 0xDFFF UTF-8 packed (U+0080 to U+07FF) [USES LAYOUT]
|
||||
// 0xE000 - 0xE0FF Modifier key (bitmap, 8 keys, Shift/Ctrl/Alt/GUI)
|
||||
// 0xE200 - 0xE2FF System key (HID usage code, page 1)
|
||||
// 0xE400 - 0xE7FF Media/Consumer key (HID usage code, page 12)
|
||||
// 0xF000 - 0xFFFF Normal key (HID usage code, page 7)
|
||||
func (kb *keyboard) Press(c Keycode) error {
|
||||
if err := kb.Down(c); nil != err {
|
||||
return err
|
||||
}
|
||||
return kb.Up(c)
|
||||
}
|
||||
|
||||
func (kb *keyboard) sendKey(consumer bool, b []byte) bool {
|
||||
kb.tx(b)
|
||||
return true
|
||||
}
|
||||
|
||||
func (kb *keyboard) keyboardSendKeys(consumer bool) bool {
|
||||
var b [9]byte
|
||||
b[0] = 0x02
|
||||
b[1] = kb.mod
|
||||
b[2] = 0x02
|
||||
b[3] = kb.key[0]
|
||||
b[4] = kb.key[1]
|
||||
b[5] = kb.key[2]
|
||||
b[6] = kb.key[3]
|
||||
b[7] = kb.key[4]
|
||||
b[8] = kb.key[5]
|
||||
return kb.sendKey(consumer, b[:])
|
||||
}
|
||||
|
||||
// Down transmits a key-down event for the given Keycode.
|
||||
//
|
||||
// The host will interpret the key as being held down continuously until a
|
||||
// corresponding key-up event is transmitted, e.g., via method Up().
|
||||
//
|
||||
// See godoc comment on method Press() for details on what input is accepted and
|
||||
// how it is interpreted.
|
||||
func (kb *keyboard) Down(c Keycode) error {
|
||||
var res uint8
|
||||
msb := c >> 8
|
||||
if msb >= 0xC2 {
|
||||
if msb < 0xE0 {
|
||||
c = ((msb & 0x1F) << 6) | Keycode(c.key())
|
||||
} else {
|
||||
switch msb {
|
||||
case 0xF0:
|
||||
return kb.down(uint8(c), 0)
|
||||
|
||||
case 0xE0:
|
||||
return kb.down(0, uint8(c))
|
||||
|
||||
case 0xE2:
|
||||
return kb.downSys(uint8(c))
|
||||
|
||||
default:
|
||||
if 0xE4 <= msb && msb <= 0xE7 {
|
||||
return kb.downCon(uint16(c & 0x03FF))
|
||||
}
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
}
|
||||
}
|
||||
c = keycode(uint16(c))
|
||||
if 0 == c {
|
||||
return ErrInvalidCodepoint
|
||||
}
|
||||
if d := deadkey(c); 0 != d {
|
||||
res = kb.mod
|
||||
if 0 != res {
|
||||
kb.mod = 0
|
||||
kb.keyboardSendKeys(false)
|
||||
}
|
||||
kb.down(d.key(), d.mod())
|
||||
kb.up(d.key(), d.mod())
|
||||
}
|
||||
return kb.down(c.key(), c.mod()|res)
|
||||
}
|
||||
|
||||
func (kb *keyboard) down(key uint8, mod uint8) error {
|
||||
send := false
|
||||
if 0 != mod {
|
||||
if kb.mod&mod != mod {
|
||||
kb.mod |= mod
|
||||
send = true
|
||||
}
|
||||
}
|
||||
if 0 != key {
|
||||
for _, k := range kb.key {
|
||||
if k == key {
|
||||
goto end
|
||||
}
|
||||
}
|
||||
for i, k := range kb.key {
|
||||
if 0 == k {
|
||||
kb.key[i] = key
|
||||
send = true
|
||||
goto end
|
||||
}
|
||||
}
|
||||
return ErrKeypressMaximum
|
||||
}
|
||||
end:
|
||||
if send {
|
||||
if !kb.keyboardSendKeys(false) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *keyboard) downCon(key uint16) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for _, k := range kb.con {
|
||||
if key == k {
|
||||
return nil // already pressed
|
||||
}
|
||||
}
|
||||
for i, k := range kb.con {
|
||||
if 0 == k {
|
||||
kb.con[i] = key
|
||||
if !kb.keyboardSendKeys(true) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrKeypressMaximum
|
||||
}
|
||||
|
||||
func (kb *keyboard) downSys(key uint8) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for _, k := range kb.sys {
|
||||
if key == k {
|
||||
return nil // already pressed
|
||||
}
|
||||
}
|
||||
for i, k := range kb.sys {
|
||||
if 0 == k {
|
||||
kb.sys[i] = key
|
||||
if !kb.keyboardSendKeys(true) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrKeypressMaximum
|
||||
}
|
||||
|
||||
// Up transmits a key-up event for the given Keycode.
|
||||
//
|
||||
// See godoc comment on method Press() for details on what input is accepted and
|
||||
// how it is interpreted.
|
||||
func (kb *keyboard) Up(c Keycode) error {
|
||||
msb := c >> 8
|
||||
if msb >= 0xC2 {
|
||||
if msb < 0xE0 {
|
||||
c = ((msb & 0x1F) << 6) | Keycode(c.key())
|
||||
} else {
|
||||
switch msb {
|
||||
case 0xF0:
|
||||
return kb.up(uint8(c), 0)
|
||||
|
||||
case 0xE0:
|
||||
return kb.up(0, uint8(c))
|
||||
|
||||
case 0xE2:
|
||||
return kb.upSys(uint8(c))
|
||||
|
||||
default:
|
||||
if 0xE4 <= msb && msb <= 0xE7 {
|
||||
return kb.upCon(uint16(c & 0x03FF))
|
||||
}
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
}
|
||||
}
|
||||
c = keycode(uint16(c))
|
||||
if 0 == c {
|
||||
return ErrInvalidCodepoint
|
||||
}
|
||||
return kb.up(c.key(), c.mod())
|
||||
}
|
||||
|
||||
// Release transmits a key-up event for all keyboard keys currently pressed as
|
||||
// if the user removed his/her hands from the keyboard entirely.
|
||||
func (kb *keyboard) Release() error {
|
||||
|
||||
bits := uint16(kb.mod)
|
||||
kb.mod = 0
|
||||
for i, k := range kb.key {
|
||||
bits |= uint16(k)
|
||||
kb.key[i] = 0
|
||||
}
|
||||
if 0 != bits {
|
||||
if !kb.keyboardSendKeys(false) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
bits = 0
|
||||
for i, k := range kb.con {
|
||||
bits |= k
|
||||
kb.con[i] = 0
|
||||
}
|
||||
for i, k := range kb.sys {
|
||||
bits |= uint16(k)
|
||||
kb.sys[i] = 0
|
||||
}
|
||||
if 0 != bits {
|
||||
if !kb.keyboardSendKeys(true) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *keyboard) up(key uint8, mod uint8) error {
|
||||
send := false
|
||||
if 0 != mod {
|
||||
if kb.mod&mod != 0 {
|
||||
kb.mod &^= mod
|
||||
send = true
|
||||
}
|
||||
}
|
||||
if 0 != key {
|
||||
for i, k := range kb.key {
|
||||
if key == k {
|
||||
kb.key[i] = 0
|
||||
send = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if send {
|
||||
if !kb.keyboardSendKeys(false) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *keyboard) upCon(key uint16) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for i, k := range kb.con {
|
||||
if key == k {
|
||||
kb.con[i] = 0
|
||||
if !kb.keyboardSendKeys(true) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *keyboard) upSys(key uint8) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for i, k := range kb.sys {
|
||||
if key == k {
|
||||
kb.sys[i] = 0
|
||||
if !kb.keyboardSendKeys(true) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,532 @@
|
||||
package keyboard
|
||||
|
||||
// Keycode is a package-defined bitmap used to encode the value of a given key.
|
||||
type Keycode uint16
|
||||
|
||||
// keycode returns the given Unicode codepoint translated to a Keycode sequence.
|
||||
// Unicode codepoints greater than U+FFFF are unsupported.
|
||||
//go:inline
|
||||
func keycode(p uint16) Keycode {
|
||||
if p < 0x80 {
|
||||
return ascii[p]
|
||||
} else if p >= 0xA0 && p < 0x0100 {
|
||||
return iso88591[p-0xA0]
|
||||
} else if uint16(UNICODE20AC) == p {
|
||||
return UNICODE20AC.mask()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func deadkey(c Keycode) Keycode {
|
||||
switch c & deadkeysMask {
|
||||
case acuteAccentBits:
|
||||
return deadkeyAcuteAccent
|
||||
case circumflexBits:
|
||||
return deadkeyCircumflex
|
||||
case diaeresisBits:
|
||||
return deadkeyDiaeresis
|
||||
case graveAccentBits:
|
||||
return deadkeyGraveAccent
|
||||
case tildeBits:
|
||||
return deadkeyTilde
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (c Keycode) mask() Keycode { return c & keycodeMask }
|
||||
|
||||
//go:inline
|
||||
func (c Keycode) key() uint8 { return uint8(c & keyMask) }
|
||||
|
||||
//go:inline
|
||||
func (c Keycode) mod() uint8 {
|
||||
var m Keycode
|
||||
if 0 != c&shiftMask {
|
||||
m |= KeyModifierShift
|
||||
}
|
||||
if 0 != c&altgrMask {
|
||||
m |= KeyModifierRightAlt
|
||||
}
|
||||
return uint8(m)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func (c Keycode) Shift() Keycode { return c | KeyModifierShift }
|
||||
|
||||
const (
|
||||
hidKeyboardKeyCount = 6 // Max number of simultaneous keypresses
|
||||
hidKeyboardSysCount = 3
|
||||
hidKeyboardConCount = 4
|
||||
)
|
||||
|
||||
// Keycodes common to all Keyboard layouts
|
||||
const (
|
||||
KeyModifierCtrl Keycode = 0x01 | 0xE000
|
||||
KeyModifierShift Keycode = 0x02 | 0xE000
|
||||
KeyModifierAlt Keycode = 0x04 | 0xE000
|
||||
KeyModifierGUI Keycode = 0x08 | 0xE000
|
||||
KeyModifierLeftCtrl Keycode = 0x01 | 0xE000
|
||||
KeyModifierLeftShift Keycode = 0x02 | 0xE000
|
||||
KeyModifierLeftAlt Keycode = 0x04 | 0xE000
|
||||
KeyModifierLeftGUI Keycode = 0x08 | 0xE000
|
||||
KeyModifierRightCtrl Keycode = 0x10 | 0xE000
|
||||
KeyModifierRightShift Keycode = 0x20 | 0xE000
|
||||
KeyModifierRightAlt Keycode = 0x40 | 0xE000
|
||||
KeyModifierRightGUI Keycode = 0x80 | 0xE000
|
||||
|
||||
KeySystemPowerDown Keycode = 0x81 | 0xE200
|
||||
KeySystemSleep Keycode = 0x82 | 0xE200
|
||||
KeySystemWakeUp Keycode = 0x83 | 0xE200
|
||||
|
||||
KeyMediaPlay Keycode = 0xB0 | 0xE400
|
||||
KeyMediaPause Keycode = 0xB1 | 0xE400
|
||||
KeyMediaRecord Keycode = 0xB2 | 0xE400
|
||||
KeyMediaFastForward Keycode = 0xB3 | 0xE400
|
||||
KeyMediaRewind Keycode = 0xB4 | 0xE400
|
||||
KeyMediaNextTrack Keycode = 0xB5 | 0xE400
|
||||
KeyMediaPrevTrack Keycode = 0xB6 | 0xE400
|
||||
KeyMediaStop Keycode = 0xB7 | 0xE400
|
||||
KeyMediaEject Keycode = 0xB8 | 0xE400
|
||||
KeyMediaRandomPlay Keycode = 0xB9 | 0xE400
|
||||
KeyMediaPlayPause Keycode = 0xCD | 0xE400
|
||||
KeyMediaPlaySkip Keycode = 0xCE | 0xE400
|
||||
KeyMediaMute Keycode = 0xE2 | 0xE400
|
||||
KeyMediaVolumeInc Keycode = 0xE9 | 0xE400
|
||||
KeyMediaVolumeDec Keycode = 0xEA | 0xE400
|
||||
|
||||
KeyA Keycode = 4 | 0xF000
|
||||
KeyB Keycode = 5 | 0xF000
|
||||
KeyC Keycode = 6 | 0xF000
|
||||
KeyD Keycode = 7 | 0xF000
|
||||
KeyE Keycode = 8 | 0xF000
|
||||
KeyF Keycode = 9 | 0xF000
|
||||
KeyG Keycode = 10 | 0xF000
|
||||
KeyH Keycode = 11 | 0xF000
|
||||
KeyI Keycode = 12 | 0xF000
|
||||
KeyJ Keycode = 13 | 0xF000
|
||||
KeyK Keycode = 14 | 0xF000
|
||||
KeyL Keycode = 15 | 0xF000
|
||||
KeyM Keycode = 16 | 0xF000
|
||||
KeyN Keycode = 17 | 0xF000
|
||||
KeyO Keycode = 18 | 0xF000
|
||||
KeyP Keycode = 19 | 0xF000
|
||||
KeyQ Keycode = 20 | 0xF000
|
||||
KeyR Keycode = 21 | 0xF000
|
||||
KeyS Keycode = 22 | 0xF000
|
||||
KeyT Keycode = 23 | 0xF000
|
||||
KeyU Keycode = 24 | 0xF000
|
||||
KeyV Keycode = 25 | 0xF000
|
||||
KeyW Keycode = 26 | 0xF000
|
||||
KeyX Keycode = 27 | 0xF000
|
||||
KeyY Keycode = 28 | 0xF000
|
||||
KeyZ Keycode = 29 | 0xF000
|
||||
Key1 Keycode = 30 | 0xF000
|
||||
Key2 Keycode = 31 | 0xF000
|
||||
Key3 Keycode = 32 | 0xF000
|
||||
Key4 Keycode = 33 | 0xF000
|
||||
Key5 Keycode = 34 | 0xF000
|
||||
Key6 Keycode = 35 | 0xF000
|
||||
Key7 Keycode = 36 | 0xF000
|
||||
Key8 Keycode = 37 | 0xF000
|
||||
Key9 Keycode = 38 | 0xF000
|
||||
Key0 Keycode = 39 | 0xF000
|
||||
KeyEnter Keycode = 40 | 0xF000
|
||||
KeyEsc Keycode = 41 | 0xF000
|
||||
KeyBackspace Keycode = 42 | 0xF000
|
||||
KeyTab Keycode = 43 | 0xF000
|
||||
KeySpace Keycode = 44 | 0xF000
|
||||
KeyMinus Keycode = 45 | 0xF000
|
||||
KeyEqual Keycode = 46 | 0xF000
|
||||
KeyLeftBrace Keycode = 47 | 0xF000
|
||||
KeyRightBrace Keycode = 48 | 0xF000
|
||||
KeyBackslash Keycode = 49 | 0xF000
|
||||
KeyNonUsNum Keycode = 50 | 0xF000
|
||||
KeySemicolon Keycode = 51 | 0xF000
|
||||
KeyQuote Keycode = 52 | 0xF000
|
||||
KeyTilde Keycode = 53 | 0xF000
|
||||
KeyComma Keycode = 54 | 0xF000
|
||||
KeyPeriod Keycode = 55 | 0xF000
|
||||
KeySlash Keycode = 56 | 0xF000
|
||||
KeyCapsLock Keycode = 57 | 0xF000
|
||||
KeyF1 Keycode = 58 | 0xF000
|
||||
KeyF2 Keycode = 59 | 0xF000
|
||||
KeyF3 Keycode = 60 | 0xF000
|
||||
KeyF4 Keycode = 61 | 0xF000
|
||||
KeyF5 Keycode = 62 | 0xF000
|
||||
KeyF6 Keycode = 63 | 0xF000
|
||||
KeyF7 Keycode = 64 | 0xF000
|
||||
KeyF8 Keycode = 65 | 0xF000
|
||||
KeyF9 Keycode = 66 | 0xF000
|
||||
KeyF10 Keycode = 67 | 0xF000
|
||||
KeyF11 Keycode = 68 | 0xF000
|
||||
KeyF12 Keycode = 69 | 0xF000
|
||||
KeyPrintscreen Keycode = 70 | 0xF000
|
||||
KeyScrollLock Keycode = 71 | 0xF000
|
||||
KeyPause Keycode = 72 | 0xF000
|
||||
KeyInsert Keycode = 73 | 0xF000
|
||||
KeyHome Keycode = 74 | 0xF000
|
||||
KeyPageUp Keycode = 75 | 0xF000
|
||||
KeyDelete Keycode = 76 | 0xF000
|
||||
KeyEnd Keycode = 77 | 0xF000
|
||||
KeyPageDown Keycode = 78 | 0xF000
|
||||
KeyRight Keycode = 79 | 0xF000
|
||||
KeyLeft Keycode = 80 | 0xF000
|
||||
KeyDown Keycode = 81 | 0xF000
|
||||
KeyUp Keycode = 82 | 0xF000
|
||||
KeyNumLock Keycode = 83 | 0xF000
|
||||
KeypadSlash Keycode = 84 | 0xF000
|
||||
KeypadAsterisk Keycode = 85 | 0xF000
|
||||
KeypadMinus Keycode = 86 | 0xF000
|
||||
KeypadPlus Keycode = 87 | 0xF000
|
||||
KeypadEnter Keycode = 88 | 0xF000
|
||||
Keypad1 Keycode = 89 | 0xF000
|
||||
Keypad2 Keycode = 90 | 0xF000
|
||||
Keypad3 Keycode = 91 | 0xF000
|
||||
Keypad4 Keycode = 92 | 0xF000
|
||||
Keypad5 Keycode = 93 | 0xF000
|
||||
Keypad6 Keycode = 94 | 0xF000
|
||||
Keypad7 Keycode = 95 | 0xF000
|
||||
Keypad8 Keycode = 96 | 0xF000
|
||||
Keypad9 Keycode = 97 | 0xF000
|
||||
Keypad0 Keycode = 98 | 0xF000
|
||||
KeypadPeriod Keycode = 99 | 0xF000
|
||||
KeyNonUSBS Keycode = 100 | 0xF000
|
||||
KeyMenu Keycode = 101 | 0xF000
|
||||
KeyF13 Keycode = 104 | 0xF000
|
||||
KeyF14 Keycode = 105 | 0xF000
|
||||
KeyF15 Keycode = 106 | 0xF000
|
||||
KeyF16 Keycode = 107 | 0xF000
|
||||
KeyF17 Keycode = 108 | 0xF000
|
||||
KeyF18 Keycode = 109 | 0xF000
|
||||
KeyF19 Keycode = 110 | 0xF000
|
||||
KeyF20 Keycode = 111 | 0xF000
|
||||
KeyF21 Keycode = 112 | 0xF000
|
||||
KeyF22 Keycode = 113 | 0xF000
|
||||
KeyF23 Keycode = 114 | 0xF000
|
||||
KeyF24 Keycode = 115 | 0xF000
|
||||
|
||||
KeyUpArrow Keycode = KeyUp
|
||||
KeyDownArrow Keycode = KeyDown
|
||||
KeyLeftArrow Keycode = KeyLeft
|
||||
KeyRightArrow Keycode = KeyRight
|
||||
KeyReturn Keycode = KeyEnter
|
||||
KeyLeftCtrl Keycode = KeyModifierLeftCtrl
|
||||
KeyLeftShift Keycode = KeyModifierLeftShift
|
||||
KeyLeftAlt Keycode = KeyModifierLeftAlt
|
||||
KeyLeftGUI Keycode = KeyModifierLeftGUI
|
||||
KeyRightCtrl Keycode = KeyModifierRightCtrl
|
||||
KeyRightShift Keycode = KeyModifierRightShift
|
||||
KeyRightAlt Keycode = KeyModifierRightAlt
|
||||
KeyRightGUI Keycode = KeyModifierRightGUI
|
||||
)
|
||||
|
||||
// Keycodes for layout US English (0x0904)
|
||||
const (
|
||||
keycodeMask Keycode = 0x07FF
|
||||
keyMask Keycode = 0x003F
|
||||
|
||||
shiftMask Keycode = 0x0040
|
||||
altgrMask Keycode = 0x0080
|
||||
deadkeysMask Keycode = 0x0700
|
||||
circumflexBits Keycode = 0x0100
|
||||
acuteAccentBits Keycode = 0x0200
|
||||
graveAccentBits Keycode = 0x0300
|
||||
tildeBits Keycode = 0x0400
|
||||
diaeresisBits Keycode = 0x0500
|
||||
deadkeyCircumflex Keycode = Key6 | shiftMask
|
||||
deadkeyAcuteAccent Keycode = KeyQuote
|
||||
deadkeyGraveAccent Keycode = KeyTilde
|
||||
deadkeyTilde Keycode = KeyTilde | shiftMask
|
||||
deadkeyDiaeresis Keycode = KeyQuote | shiftMask
|
||||
|
||||
ASCII00 Keycode = 0 // 0 NUL
|
||||
ASCII01 Keycode = 0 // 1 SOH
|
||||
ASCII02 Keycode = 0 // 2 STX
|
||||
ASCII03 Keycode = 0 // 3 ETX
|
||||
ASCII04 Keycode = 0 // 4 EOT
|
||||
ASCII05 Keycode = 0 // 5 ENQ
|
||||
ASCII06 Keycode = 0 // 6 ACK
|
||||
ASCII07 Keycode = 0 // 7 BEL
|
||||
ASCII08 Keycode = KeyBackspace // 8 BS
|
||||
ASCII09 Keycode = KeyTab // 9 TAB
|
||||
ASCII0A Keycode = KeyEnter // 10 LF
|
||||
ASCII0B Keycode = 0 // 11 VT
|
||||
ASCII0C Keycode = 0 // 12 FF
|
||||
ASCII0D Keycode = 0 // 13 CR
|
||||
ASCII0E Keycode = 0 // 14 SO
|
||||
ASCII0F Keycode = 0 // 15 SI
|
||||
ASCII10 Keycode = 0 // 16 DEL
|
||||
ASCII11 Keycode = 0 // 17 DC1
|
||||
ASCII12 Keycode = 0 // 18 DC2
|
||||
ASCII13 Keycode = 0 // 19 DC3
|
||||
ASCII14 Keycode = 0 // 20 DC4
|
||||
ASCII15 Keycode = 0 // 21 NAK
|
||||
ASCII16 Keycode = 0 // 22 SYN
|
||||
ASCII17 Keycode = 0 // 23 ETB
|
||||
ASCII18 Keycode = 0 // 24 CAN
|
||||
ASCII19 Keycode = 0 // 25 EM
|
||||
ASCII1A Keycode = 0 // 26 SUB
|
||||
ASCII1B Keycode = 0 // 27 ESC
|
||||
ASCII1C Keycode = 0 // 28 FS
|
||||
ASCII1D Keycode = 0 // 29 GS
|
||||
ASCII1E Keycode = 0 // 30 RS
|
||||
ASCII1F Keycode = 0 // 31 US
|
||||
|
||||
ASCII20 Keycode = KeySpace // 32 SPACE
|
||||
ASCII21 Keycode = Key1 | shiftMask // 33 !
|
||||
ASCII22 Keycode = diaeresisBits | KeySpace // 34 "
|
||||
ASCII23 Keycode = Key3 | shiftMask // 35 #
|
||||
ASCII24 Keycode = Key4 | shiftMask // 36 $
|
||||
ASCII25 Keycode = Key5 | shiftMask // 37 %
|
||||
ASCII26 Keycode = Key7 | shiftMask // 38 &
|
||||
ASCII27 Keycode = acuteAccentBits | KeySpace // 39 '
|
||||
ASCII28 Keycode = Key9 | shiftMask // 40 (
|
||||
ASCII29 Keycode = Key0 | shiftMask // 41 )
|
||||
ASCII2A Keycode = Key8 | shiftMask // 42 *
|
||||
ASCII2B Keycode = KeyEqual | shiftMask // 43 +
|
||||
ASCII2C Keycode = KeyComma // 44 ,
|
||||
ASCII2D Keycode = KeyMinus // 45 -
|
||||
ASCII2E Keycode = KeyPeriod // 46 .
|
||||
ASCII2F Keycode = KeySlash // 47 /
|
||||
ASCII30 Keycode = Key0 // 48 0
|
||||
ASCII31 Keycode = Key1 // 49 1
|
||||
ASCII32 Keycode = Key2 // 50 2
|
||||
ASCII33 Keycode = Key3 // 51 3
|
||||
ASCII34 Keycode = Key4 // 52 4
|
||||
ASCII35 Keycode = Key5 // 53 5
|
||||
ASCII36 Keycode = Key6 // 54 6
|
||||
ASCII37 Keycode = Key7 // 55 7
|
||||
ASCII38 Keycode = Key8 // 55 8
|
||||
ASCII39 Keycode = Key9 // 57 9
|
||||
ASCII3A Keycode = KeySemicolon | shiftMask // 58 :
|
||||
ASCII3B Keycode = KeySemicolon // 59 ;
|
||||
ASCII3C Keycode = KeyComma | shiftMask // 60 <
|
||||
ASCII3D Keycode = KeyEqual // 61 =
|
||||
ASCII3E Keycode = KeyPeriod | shiftMask // 62 >
|
||||
ASCII3F Keycode = KeySlash | shiftMask // 63 ?
|
||||
ASCII40 Keycode = Key2 | shiftMask // 64 @
|
||||
ASCII41 Keycode = KeyA | shiftMask // 65 A
|
||||
ASCII42 Keycode = KeyB | shiftMask // 66 B
|
||||
ASCII43 Keycode = KeyC | shiftMask // 67 C
|
||||
ASCII44 Keycode = KeyD | shiftMask // 68 D
|
||||
ASCII45 Keycode = KeyE | shiftMask // 69 E
|
||||
ASCII46 Keycode = KeyF | shiftMask // 70 F
|
||||
ASCII47 Keycode = KeyG | shiftMask // 71 G
|
||||
ASCII48 Keycode = KeyH | shiftMask // 72 H
|
||||
ASCII49 Keycode = KeyI | shiftMask // 73 I
|
||||
ASCII4A Keycode = KeyJ | shiftMask // 74 J
|
||||
ASCII4B Keycode = KeyK | shiftMask // 75 K
|
||||
ASCII4C Keycode = KeyL | shiftMask // 76 L
|
||||
ASCII4D Keycode = KeyM | shiftMask // 77 M
|
||||
ASCII4E Keycode = KeyN | shiftMask // 78 N
|
||||
ASCII4F Keycode = KeyO | shiftMask // 79 O
|
||||
ASCII50 Keycode = KeyP | shiftMask // 80 P
|
||||
ASCII51 Keycode = KeyQ | shiftMask // 81 Q
|
||||
ASCII52 Keycode = KeyR | shiftMask // 82 R
|
||||
ASCII53 Keycode = KeyS | shiftMask // 83 S
|
||||
ASCII54 Keycode = KeyT | shiftMask // 84 T
|
||||
ASCII55 Keycode = KeyU | shiftMask // 85 U
|
||||
ASCII56 Keycode = KeyV | shiftMask // 86 V
|
||||
ASCII57 Keycode = KeyW | shiftMask // 87 W
|
||||
ASCII58 Keycode = KeyX | shiftMask // 88 X
|
||||
ASCII59 Keycode = KeyY | shiftMask // 89 Y
|
||||
ASCII5A Keycode = KeyZ | shiftMask // 90 Z
|
||||
ASCII5B Keycode = KeyLeftBrace // 91 [
|
||||
ASCII5C Keycode = KeyBackslash // 92 \
|
||||
ASCII5D Keycode = KeyRightBrace // 93 ]
|
||||
ASCII5E Keycode = circumflexBits | KeySpace // 94 ^
|
||||
ASCII5F Keycode = KeyMinus | shiftMask // 95
|
||||
ASCII60 Keycode = graveAccentBits | KeySpace // 96 `
|
||||
ASCII61 Keycode = KeyA // 97 a
|
||||
ASCII62 Keycode = KeyB // 98 b
|
||||
ASCII63 Keycode = KeyC // 99 c
|
||||
ASCII64 Keycode = KeyD // 100 d
|
||||
ASCII65 Keycode = KeyE // 101 e
|
||||
ASCII66 Keycode = KeyF // 102 f
|
||||
ASCII67 Keycode = KeyG // 103 g
|
||||
ASCII68 Keycode = KeyH // 104 h
|
||||
ASCII69 Keycode = KeyI // 105 i
|
||||
ASCII6A Keycode = KeyJ // 106 j
|
||||
ASCII6B Keycode = KeyK // 107 k
|
||||
ASCII6C Keycode = KeyL // 108 l
|
||||
ASCII6D Keycode = KeyM // 109 m
|
||||
ASCII6E Keycode = KeyN // 110 n
|
||||
ASCII6F Keycode = KeyO // 111 o
|
||||
ASCII70 Keycode = KeyP // 112 p
|
||||
ASCII71 Keycode = KeyQ // 113 q
|
||||
ASCII72 Keycode = KeyR // 114 r
|
||||
ASCII73 Keycode = KeyS // 115 s
|
||||
ASCII74 Keycode = KeyT // 116 t
|
||||
ASCII75 Keycode = KeyU // 117 u
|
||||
ASCII76 Keycode = KeyV // 118 v
|
||||
ASCII77 Keycode = KeyW // 119 w
|
||||
ASCII78 Keycode = KeyX // 120 x
|
||||
ASCII79 Keycode = KeyY // 121 y
|
||||
ASCII7A Keycode = KeyZ // 122 z
|
||||
ASCII7B Keycode = KeyLeftBrace | shiftMask // 123 {
|
||||
ASCII7C Keycode = KeyBackslash | shiftMask // 124 |
|
||||
ASCII7D Keycode = KeyRightBrace | shiftMask // 125 }
|
||||
ASCII7E Keycode = tildeBits | KeySpace // 126 ~
|
||||
ASCII7F Keycode = KeyBackspace // 127 DEL
|
||||
ISO88591A0 Keycode = KeySpace // 160 Nonbreakng Space
|
||||
ISO88591A1 Keycode = Key1 | altgrMask // 161 ¡ Inverted Exclamation
|
||||
ISO88591A2 Keycode = KeyC | altgrMask | shiftMask // 162 ¢ Cent SIGN
|
||||
ISO88591A3 Keycode = Key4 | altgrMask | shiftMask // 163 £ Pound Sign
|
||||
ISO88591A4 Keycode = Key4 | altgrMask // 164 ¤ Currency or Euro Sign
|
||||
ISO88591A5 Keycode = KeyMinus | altgrMask // 165 ¥ YEN SIGN
|
||||
ISO88591A6 Keycode = KeyBackslash | altgrMask | shiftMask // 166 ¦ BROKEN BAR ??
|
||||
ISO88591A7 Keycode = KeyS | altgrMask | shiftMask // 167 § SECTION SIGN
|
||||
ISO88591A8 Keycode = KeyQuote | altgrMask | shiftMask // 168 ¨ DIAERESIS
|
||||
ISO88591A9 Keycode = KeyC | altgrMask // 169 © COPYRIGHT SIGN
|
||||
ISO88591AA Keycode = 0 // 170 ª FEMININE ORDINAL
|
||||
ISO88591AB Keycode = KeyLeftBrace | altgrMask // 171 « LEFT DOUBLE ANGLE QUOTE
|
||||
ISO88591AC Keycode = KeyBackslash | altgrMask // 172 ¬ NOT SIGN ??
|
||||
ISO88591AD Keycode = 0 // 173 SOFT HYPHEN
|
||||
ISO88591AE Keycode = KeyR | altgrMask // 174 ® REGISTERED SIGN
|
||||
ISO88591AF Keycode = 0 // 175 ¯ MACRON
|
||||
ISO88591B0 Keycode = KeySemicolon | altgrMask | shiftMask // 176 ° DEGREE SIGN
|
||||
ISO88591B1 Keycode = 0 // 177 ± PLUS-MINUS SIGN
|
||||
ISO88591B2 Keycode = Key2 | altgrMask // 178 ² SUPERSCRIPT TWO
|
||||
ISO88591B3 Keycode = Key3 | altgrMask // 179 ³ SUPERSCRIPT THREE
|
||||
ISO88591B4 Keycode = KeyQuote | altgrMask // 180 ´ ACUTE ACCENT
|
||||
ISO88591B5 Keycode = KeyM | altgrMask // 181 µ MICRO SIGN
|
||||
ISO88591B6 Keycode = KeySemicolon | altgrMask // 182 ¶ PILCROW SIGN
|
||||
ISO88591B7 Keycode = 0 // 183 · MIDDLE DOT
|
||||
ISO88591B8 Keycode = 0 // 184 ¸ CEDILLA
|
||||
ISO88591B9 Keycode = Key1 | altgrMask | shiftMask // 185 ¹ SUPERSCRIPT ONE
|
||||
ISO88591BA Keycode = 0 // 186 º MASCULINE ORDINAL
|
||||
ISO88591BB Keycode = KeyRightBrace | altgrMask // 187 » RIGHT DOUBLE ANGLE QUOTE
|
||||
ISO88591BC Keycode = Key6 | altgrMask // 188 ¼ FRACTION ONE QUARTER
|
||||
ISO88591BD Keycode = Key7 | altgrMask // 189 ½ FRACTION ONE HALF
|
||||
ISO88591BE Keycode = Key8 | altgrMask // 190 ¾ FRACTION THREE QUARTERS
|
||||
ISO88591BF Keycode = KeySlash | altgrMask // 191 ¿ INVERTED QUESTION MARK
|
||||
ISO88591C0 Keycode = graveAccentBits | KeyA | shiftMask // 192 À A GRAVE
|
||||
ISO88591C1 Keycode = KeyA | altgrMask | shiftMask // 193 Á A ACUTE
|
||||
ISO88591C2 Keycode = circumflexBits | KeyA | shiftMask // 194 Â A CIRCUMFLEX
|
||||
ISO88591C3 Keycode = tildeBits | KeyA | shiftMask // 195 Ã A TILDE
|
||||
ISO88591C4 Keycode = KeyQ | altgrMask | shiftMask // 196 Ä A DIAERESIS
|
||||
ISO88591C5 Keycode = KeyW | altgrMask | shiftMask // 197 Å A RING ABOVE
|
||||
ISO88591C6 Keycode = KeyZ | altgrMask | shiftMask // 198 Æ AE
|
||||
ISO88591C7 Keycode = KeyComma | altgrMask | shiftMask // 199 Ç C CEDILLA
|
||||
ISO88591C8 Keycode = graveAccentBits | KeyE | shiftMask // 200 È E GRAVE
|
||||
ISO88591C9 Keycode = KeyE | altgrMask | shiftMask // 201 É E ACUTE
|
||||
ISO88591CA Keycode = circumflexBits | KeyE | shiftMask // 202 Ê E CIRCUMFLEX
|
||||
ISO88591CB Keycode = diaeresisBits | KeyE | shiftMask // 203 Ë E DIAERESIS
|
||||
ISO88591CC Keycode = graveAccentBits | KeyI | shiftMask // 204 Ì I GRAVE
|
||||
ISO88591CD Keycode = KeyI | altgrMask | shiftMask // 205 Í I ACUTE
|
||||
ISO88591CE Keycode = circumflexBits | KeyI | shiftMask // 206 Î I CIRCUMFLEX
|
||||
ISO88591CF Keycode = diaeresisBits | KeyI | shiftMask // 207 Ï I DIAERESIS
|
||||
ISO88591D0 Keycode = KeyD | altgrMask | shiftMask // 208 Ð ETH
|
||||
ISO88591D1 Keycode = KeyN | altgrMask | shiftMask // 209 Ñ N TILDE
|
||||
ISO88591D2 Keycode = graveAccentBits | KeyO | shiftMask // 210 Ò O GRAVE
|
||||
ISO88591D3 Keycode = KeyO | altgrMask | shiftMask // 211 Ó O ACUTE
|
||||
ISO88591D4 Keycode = circumflexBits | KeyO | shiftMask // 212 Ô O CIRCUMFLEX
|
||||
ISO88591D5 Keycode = tildeBits | KeyO | shiftMask // 213 Õ O TILDE
|
||||
ISO88591D6 Keycode = KeyP | altgrMask | shiftMask // 214 Ö O DIAERESIS
|
||||
ISO88591D7 Keycode = KeyEqual | altgrMask // 215 × MULTIPLICATION
|
||||
ISO88591D8 Keycode = KeyL | altgrMask | shiftMask // 216 Ø O STROKE
|
||||
ISO88591D9 Keycode = graveAccentBits | KeyU | shiftMask // 217 Ù U GRAVE
|
||||
ISO88591DA Keycode = KeyU | altgrMask | shiftMask // 218 Ú U ACUTE
|
||||
ISO88591DB Keycode = circumflexBits | KeyU | shiftMask // 219 Û U CIRCUMFLEX
|
||||
ISO88591DC Keycode = KeyY | altgrMask | shiftMask // 220 Ü U DIAERESIS
|
||||
ISO88591DD Keycode = acuteAccentBits | KeyY | shiftMask // 221 Ý Y ACUTE
|
||||
ISO88591DE Keycode = KeyT | altgrMask | shiftMask // 222 Þ THORN
|
||||
ISO88591DF Keycode = KeyS | altgrMask // 223 ß SHARP S
|
||||
ISO88591E0 Keycode = graveAccentBits | KeyA // 224 à a GRAVE
|
||||
ISO88591E1 Keycode = KeyA | altgrMask // 225 á a ACUTE
|
||||
ISO88591E2 Keycode = circumflexBits | KeyA // 226 â a CIRCUMFLEX
|
||||
ISO88591E3 Keycode = tildeBits | KeyA // 227 ã a TILDE
|
||||
ISO88591E4 Keycode = diaeresisBits | KeyA // 228 ä a DIAERESIS
|
||||
ISO88591E5 Keycode = KeyW | altgrMask // 229 å a RING ABOVE
|
||||
ISO88591E6 Keycode = KeyZ | altgrMask // 230 æ ae
|
||||
ISO88591E7 Keycode = KeyComma | altgrMask // 231 ç c CEDILLA
|
||||
ISO88591E8 Keycode = graveAccentBits | KeyE // 232 è e GRAVE
|
||||
ISO88591E9 Keycode = acuteAccentBits | KeyE // 233 é e ACUTE
|
||||
ISO88591EA Keycode = circumflexBits | KeyE // 234 ê e CIRCUMFLEX
|
||||
ISO88591EB Keycode = diaeresisBits | KeyE // 235 ë e DIAERESIS
|
||||
ISO88591EC Keycode = graveAccentBits | KeyI // 236 ì i GRAVE
|
||||
ISO88591ED Keycode = KeyI | altgrMask // 237 í i ACUTE
|
||||
ISO88591EE Keycode = circumflexBits | KeyI // 238 î i CIRCUMFLEX
|
||||
ISO88591EF Keycode = diaeresisBits | KeyI // 239 ï i DIAERESIS
|
||||
ISO88591F0 Keycode = KeyD | altgrMask // 240 ð ETH
|
||||
ISO88591F1 Keycode = KeyN | altgrMask // 241 ñ n TILDE
|
||||
ISO88591F2 Keycode = graveAccentBits | KeyO // 242 ò o GRAVE
|
||||
ISO88591F3 Keycode = KeyO | altgrMask // 243 ó o ACUTE
|
||||
ISO88591F4 Keycode = circumflexBits | KeyO // 244 ô o CIRCUMFLEX
|
||||
ISO88591F5 Keycode = tildeBits | KeyO // 245 õ o TILDE
|
||||
ISO88591F6 Keycode = KeyP | altgrMask // 246 ö o DIAERESIS
|
||||
ISO88591F7 Keycode = KeyEqual | altgrMask | shiftMask // 247 ÷ DIVISION
|
||||
ISO88591F8 Keycode = KeyL | altgrMask // 248 ø o STROKE
|
||||
ISO88591F9 Keycode = graveAccentBits | KeyU // 249 ù u GRAVE
|
||||
ISO88591FA Keycode = KeyU | altgrMask // 250 ú u ACUTE
|
||||
ISO88591FB Keycode = circumflexBits | KeyU // 251 û u CIRCUMFLEX
|
||||
ISO88591FC Keycode = KeyY | altgrMask // 252 ü u DIAERESIS
|
||||
ISO88591FD Keycode = acuteAccentBits | KeyY // 253 ý y ACUTE
|
||||
ISO88591FE Keycode = KeyT | altgrMask // 254 þ THORN
|
||||
ISO88591FF Keycode = diaeresisBits | KeyY // 255 ÿ y DIAERESIS
|
||||
UNICODE20AC Keycode = Key5 | altgrMask // 20AC € Euro Sign
|
||||
)
|
||||
|
||||
var ascii = [...]Keycode{
|
||||
ASCII00.mask(), ASCII01.mask(), ASCII02.mask(), ASCII03.mask(),
|
||||
ASCII04.mask(), ASCII05.mask(), ASCII06.mask(), ASCII07.mask(),
|
||||
ASCII08.mask(), ASCII09.mask(), ASCII0A.mask(), ASCII0B.mask(),
|
||||
ASCII0C.mask(), ASCII0D.mask(), ASCII0E.mask(), ASCII0F.mask(),
|
||||
ASCII10.mask(), ASCII11.mask(), ASCII12.mask(), ASCII13.mask(),
|
||||
ASCII14.mask(), ASCII15.mask(), ASCII16.mask(), ASCII17.mask(),
|
||||
ASCII18.mask(), ASCII19.mask(), ASCII1A.mask(), ASCII1B.mask(),
|
||||
ASCII1C.mask(), ASCII1D.mask(), ASCII1E.mask(), ASCII1F.mask(),
|
||||
ASCII20.mask(), ASCII21.mask(), ASCII22.mask(), ASCII23.mask(),
|
||||
ASCII24.mask(), ASCII25.mask(), ASCII26.mask(), ASCII27.mask(),
|
||||
ASCII28.mask(), ASCII29.mask(), ASCII2A.mask(), ASCII2B.mask(),
|
||||
ASCII2C.mask(), ASCII2D.mask(), ASCII2E.mask(), ASCII2F.mask(),
|
||||
ASCII30.mask(), ASCII31.mask(), ASCII32.mask(), ASCII33.mask(),
|
||||
ASCII34.mask(), ASCII35.mask(), ASCII36.mask(), ASCII37.mask(),
|
||||
ASCII38.mask(), ASCII39.mask(), ASCII3A.mask(), ASCII3B.mask(),
|
||||
ASCII3C.mask(), ASCII3D.mask(), ASCII3E.mask(), ASCII3F.mask(),
|
||||
ASCII40.mask(), ASCII41.mask(), ASCII42.mask(), ASCII43.mask(),
|
||||
ASCII44.mask(), ASCII45.mask(), ASCII46.mask(), ASCII47.mask(),
|
||||
ASCII48.mask(), ASCII49.mask(), ASCII4A.mask(), ASCII4B.mask(),
|
||||
ASCII4C.mask(), ASCII4D.mask(), ASCII4E.mask(), ASCII4F.mask(),
|
||||
ASCII50.mask(), ASCII51.mask(), ASCII52.mask(), ASCII53.mask(),
|
||||
ASCII54.mask(), ASCII55.mask(), ASCII56.mask(), ASCII57.mask(),
|
||||
ASCII58.mask(), ASCII59.mask(), ASCII5A.mask(), ASCII5B.mask(),
|
||||
ASCII5C.mask(), ASCII5D.mask(), ASCII5E.mask(), ASCII5F.mask(),
|
||||
ASCII60.mask(), ASCII61.mask(), ASCII62.mask(), ASCII63.mask(),
|
||||
ASCII64.mask(), ASCII65.mask(), ASCII66.mask(), ASCII67.mask(),
|
||||
ASCII68.mask(), ASCII69.mask(), ASCII6A.mask(), ASCII6B.mask(),
|
||||
ASCII6C.mask(), ASCII6D.mask(), ASCII6E.mask(), ASCII6F.mask(),
|
||||
ASCII70.mask(), ASCII71.mask(), ASCII72.mask(), ASCII73.mask(),
|
||||
ASCII74.mask(), ASCII75.mask(), ASCII76.mask(), ASCII77.mask(),
|
||||
ASCII78.mask(), ASCII79.mask(), ASCII7A.mask(), ASCII7B.mask(),
|
||||
ASCII7C.mask(), ASCII7D.mask(), ASCII7E.mask(), ASCII7F.mask(),
|
||||
}
|
||||
|
||||
var iso88591 = [...]Keycode{
|
||||
ISO88591A0.mask(), ISO88591A1.mask(), ISO88591A2.mask(), ISO88591A3.mask(),
|
||||
ISO88591A4.mask(), ISO88591A5.mask(), ISO88591A6.mask(), ISO88591A7.mask(),
|
||||
ISO88591A8.mask(), ISO88591A9.mask(), ISO88591AA.mask(), ISO88591AB.mask(),
|
||||
ISO88591AC.mask(), ISO88591AD.mask(), ISO88591AE.mask(), ISO88591AF.mask(),
|
||||
ISO88591B0.mask(), ISO88591B1.mask(), ISO88591B2.mask(), ISO88591B3.mask(),
|
||||
ISO88591B4.mask(), ISO88591B5.mask(), ISO88591B6.mask(), ISO88591B7.mask(),
|
||||
ISO88591B8.mask(), ISO88591B9.mask(), ISO88591BA.mask(), ISO88591BB.mask(),
|
||||
ISO88591BC.mask(), ISO88591BD.mask(), ISO88591BE.mask(), ISO88591BF.mask(),
|
||||
ISO88591C0.mask(), ISO88591C1.mask(), ISO88591C2.mask(), ISO88591C3.mask(),
|
||||
ISO88591C4.mask(), ISO88591C5.mask(), ISO88591C6.mask(), ISO88591C7.mask(),
|
||||
ISO88591C8.mask(), ISO88591C9.mask(), ISO88591CA.mask(), ISO88591CB.mask(),
|
||||
ISO88591CC.mask(), ISO88591CD.mask(), ISO88591CE.mask(), ISO88591CF.mask(),
|
||||
ISO88591D0.mask(), ISO88591D1.mask(), ISO88591D2.mask(), ISO88591D3.mask(),
|
||||
ISO88591D4.mask(), ISO88591D5.mask(), ISO88591D6.mask(), ISO88591D7.mask(),
|
||||
ISO88591D8.mask(), ISO88591D9.mask(), ISO88591DA.mask(), ISO88591DB.mask(),
|
||||
ISO88591DC.mask(), ISO88591DD.mask(), ISO88591DE.mask(), ISO88591DF.mask(),
|
||||
ISO88591E0.mask(), ISO88591E1.mask(), ISO88591E2.mask(), ISO88591E3.mask(),
|
||||
ISO88591E4.mask(), ISO88591E5.mask(), ISO88591E6.mask(), ISO88591E7.mask(),
|
||||
ISO88591E8.mask(), ISO88591E9.mask(), ISO88591EA.mask(), ISO88591EB.mask(),
|
||||
ISO88591EC.mask(), ISO88591ED.mask(), ISO88591EE.mask(), ISO88591EF.mask(),
|
||||
ISO88591F0.mask(), ISO88591F1.mask(), ISO88591F2.mask(), ISO88591F3.mask(),
|
||||
ISO88591F4.mask(), ISO88591F5.mask(), ISO88591F6.mask(), ISO88591F7.mask(),
|
||||
ISO88591F8.mask(), ISO88591F9.mask(), ISO88591FA.mask(), ISO88591FB.mask(),
|
||||
ISO88591FC.mask(), ISO88591FD.mask(), ISO88591FE.mask(), ISO88591FF.mask(),
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package mouse
|
||||
|
||||
import (
|
||||
"machine/usb/hid"
|
||||
)
|
||||
|
||||
var Mouse *mouse
|
||||
|
||||
type Button byte
|
||||
|
||||
const (
|
||||
Left Button = 1 << iota
|
||||
Right
|
||||
Middle
|
||||
)
|
||||
|
||||
type mouse struct {
|
||||
buf *hid.RingBuffer
|
||||
button Button
|
||||
waitTxc bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
if Mouse == nil {
|
||||
Mouse = newMouse()
|
||||
hid.SetCallbackHandler(Mouse)
|
||||
}
|
||||
}
|
||||
|
||||
// New returns hid-mouse.
|
||||
func New() *mouse {
|
||||
return Mouse
|
||||
}
|
||||
|
||||
func newMouse() *mouse {
|
||||
return &mouse{
|
||||
buf: hid.NewRingBuffer(),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mouse) Callback() bool {
|
||||
m.waitTxc = false
|
||||
if b, ok := m.buf.Get(); ok {
|
||||
m.waitTxc = true
|
||||
hid.SendUSBPacket(b[:5])
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *mouse) tx(b []byte) {
|
||||
if m.waitTxc {
|
||||
m.buf.Put(b)
|
||||
} else {
|
||||
m.waitTxc = true
|
||||
hid.SendUSBPacket(b)
|
||||
}
|
||||
}
|
||||
|
||||
// Move is a function that moves the mouse cursor.
|
||||
func (m *mouse) Move(vx, vy int) {
|
||||
if vx == 0 && vy == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if vx < -128 {
|
||||
vx = -128
|
||||
}
|
||||
if vx > 127 {
|
||||
vx = 127
|
||||
}
|
||||
|
||||
if vy < -128 {
|
||||
vy = -128
|
||||
}
|
||||
if vy > 127 {
|
||||
vy = 127
|
||||
}
|
||||
|
||||
m.tx([]byte{
|
||||
0x01, byte(m.button), byte(vx), byte(vy), 0x00,
|
||||
})
|
||||
}
|
||||
|
||||
// Cilck clicks the mouse button.
|
||||
func (m *mouse) Click(btn Button) {
|
||||
m.Press(btn)
|
||||
m.Release(btn)
|
||||
}
|
||||
|
||||
// Press presses the given mouse buttons.
|
||||
func (m *mouse) Press(btn Button) {
|
||||
m.button |= btn
|
||||
m.tx([]byte{
|
||||
0x01, byte(m.button), 0x00, 0x00, 0x00,
|
||||
})
|
||||
}
|
||||
|
||||
// Release releases the given mouse buttons.
|
||||
func (m *mouse) Release(btn Button) {
|
||||
m.button &= ^btn
|
||||
m.tx([]byte{
|
||||
0x01, byte(m.button), 0x00, 0x00, 0x00,
|
||||
})
|
||||
}
|
||||
|
||||
// Wheel controls the mouse wheel.
|
||||
func (m *mouse) Wheel(v int) {
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if v < -128 {
|
||||
v = -128
|
||||
}
|
||||
if v > 127 {
|
||||
v = 127
|
||||
}
|
||||
|
||||
m.tx([]byte{
|
||||
0x01, byte(m.button), 0x00, 0x00, byte(v),
|
||||
})
|
||||
}
|
||||
|
||||
// WheelDown turns the mouse wheel down.
|
||||
func (m *mouse) WheelDown() {
|
||||
m.Wheel(-1)
|
||||
}
|
||||
|
||||
// WheelUp turns the mouse wheel up.
|
||||
func (m *mouse) WheelUp() {
|
||||
m.Wheel(1)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package midi
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const bufferSize = 128
|
||||
|
||||
// RingBuffer is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
type RingBuffer struct {
|
||||
rxbuffer [bufferSize][4]byte
|
||||
head volatile.Register8
|
||||
tail volatile.Register8
|
||||
}
|
||||
|
||||
// NewRingBuffer returns a new ring buffer.
|
||||
func NewRingBuffer() *RingBuffer {
|
||||
return &RingBuffer{}
|
||||
}
|
||||
|
||||
// Used returns how many bytes in buffer have been used.
|
||||
func (rb *RingBuffer) Used() uint8 {
|
||||
return uint8(rb.head.Get() - rb.tail.Get())
|
||||
}
|
||||
|
||||
// Put stores a byte in the buffer. If the buffer is already
|
||||
// full, the method will return false.
|
||||
func (rb *RingBuffer) Put(val []byte) bool {
|
||||
if rb.Used() != bufferSize {
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
copy(rb.rxbuffer[rb.head.Get()%bufferSize][:], val)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get returns a byte from the buffer. If the buffer is empty,
|
||||
// the method will return a false as the second value.
|
||||
func (rb *RingBuffer) Get() ([]byte, bool) {
|
||||
if rb.Used() != 0 {
|
||||
rb.tail.Set(rb.tail.Get() + 1)
|
||||
return rb.rxbuffer[rb.tail.Get()%bufferSize][:], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Clear resets the head and tail pointer to zero.
|
||||
func (rb *RingBuffer) Clear() {
|
||||
rb.head.Set(0)
|
||||
rb.tail.Set(0)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package midi
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
const (
|
||||
midiEndpointOut = 5 // from PC
|
||||
midiEndpointIn = 6 // to PC
|
||||
)
|
||||
|
||||
var Midi *midi
|
||||
|
||||
type midi struct {
|
||||
buf *RingBuffer
|
||||
callbackFuncRx func([]byte)
|
||||
waitTxc bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
if Midi == nil {
|
||||
Midi = newMidi()
|
||||
}
|
||||
}
|
||||
|
||||
// New returns hid-mouse.
|
||||
func New() *midi {
|
||||
return Midi
|
||||
}
|
||||
|
||||
func newMidi() *midi {
|
||||
m := &midi{
|
||||
buf: NewRingBuffer(),
|
||||
}
|
||||
machine.EnableMIDI(m.Callback, m.CallbackRx, nil)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *midi) SetCallback(callbackRx func([]byte)) {
|
||||
m.callbackFuncRx = callbackRx
|
||||
}
|
||||
|
||||
func (m *midi) Write(b []byte) (n int, err error) {
|
||||
i := 0
|
||||
for i = 0; i < len(b); i += 4 {
|
||||
m.tx(b[i : i+4])
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// sendUSBPacket sends a MIDIPacket.
|
||||
func (m *midi) sendUSBPacket(b []byte) {
|
||||
machine.SendUSBInPacket(midiEndpointIn, b)
|
||||
}
|
||||
|
||||
// from BulkIn
|
||||
func (m *midi) Callback() {
|
||||
m.waitTxc = false
|
||||
if b, ok := m.buf.Get(); ok {
|
||||
m.waitTxc = true
|
||||
m.sendUSBPacket(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *midi) tx(b []byte) {
|
||||
if m.waitTxc {
|
||||
m.buf.Put(b)
|
||||
} else {
|
||||
m.waitTxc = true
|
||||
m.sendUSBPacket(b)
|
||||
}
|
||||
}
|
||||
|
||||
// from BulkOut
|
||||
func (m *midi) CallbackRx(b []byte) {
|
||||
if m.callbackFuncRx != nil {
|
||||
m.callbackFuncRx(b)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//go:build sam || nrf52840 || rp2040
|
||||
// +build sam nrf52840 rp2040
|
||||
|
||||
package machine
|
||||
|
||||
type USBDescriptor struct {
|
||||
Device []byte
|
||||
Configuration []byte
|
||||
HID map[uint16][]byte
|
||||
}
|
||||
|
||||
func (d *USBDescriptor) Configure(idVendor, idProduct uint16) {
|
||||
d.Device[8] = byte(idVendor)
|
||||
d.Device[9] = byte(idVendor >> 8)
|
||||
d.Device[10] = byte(idProduct)
|
||||
d.Device[11] = byte(idProduct >> 8)
|
||||
|
||||
d.Configuration[2] = byte(len(d.Configuration))
|
||||
d.Configuration[3] = byte(len(d.Configuration) >> 8)
|
||||
}
|
||||
|
||||
var descriptorCDC = USBDescriptor{
|
||||
Device: []byte{
|
||||
0x12, 0x01, 0x00, 0x02, 0xef, 0x02, 0x01, 0x40, 0x86, 0x28, 0x2d, 0x80, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01,
|
||||
},
|
||||
Configuration: []byte{
|
||||
0x09, 0x02, 0x4b, 0x00, 0x02, 0x01, 0x00, 0xa0, 0x32,
|
||||
0x08, 0x0b, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,
|
||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00,
|
||||
0x05, 0x24, 0x00, 0x10, 0x01,
|
||||
0x04, 0x24, 0x02, 0x06,
|
||||
0x05, 0x24, 0x06, 0x00, 0x01,
|
||||
0x05, 0x24, 0x01, 0x01, 0x01,
|
||||
0x07, 0x05, 0x81, 0x03, 0x10, 0x00, 0x10,
|
||||
0x09, 0x04, 0x01, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
|
||||
0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00,
|
||||
},
|
||||
}
|
||||
|
||||
var descriptorCDCHID = USBDescriptor{
|
||||
Device: []byte{
|
||||
0x12, 0x01, 0x00, 0x02, 0xef, 0x02, 0x01, 0x40, 0x86, 0x28, 0x2d, 0x80, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01,
|
||||
},
|
||||
Configuration: []byte{
|
||||
0x09, 0x02, 0x64, 0x00, 0x03, 0x01, 0x00, 0xa0, 0x32,
|
||||
0x08, 0x0b, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,
|
||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00,
|
||||
0x05, 0x24, 0x00, 0x10, 0x01,
|
||||
0x04, 0x24, 0x02, 0x06,
|
||||
0x05, 0x24, 0x06, 0x00, 0x01,
|
||||
0x05, 0x24, 0x01, 0x01, 0x01,
|
||||
0x07, 0x05, 0x81, 0x03, 0x10, 0x00, 0x10,
|
||||
0x09, 0x04, 0x01, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
|
||||
0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00,
|
||||
0x09, 0x04, 0x02, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
|
||||
0x09, 0x21, 0x01, 0x01, 0x00, 0x01, 0x22, 0x65, 0x00,
|
||||
0x07, 0x05, 0x84, 0x03, 0x40, 0x00, 0x01,
|
||||
},
|
||||
HID: map[uint16][]byte{
|
||||
2: []byte{
|
||||
// keyboard and mouse
|
||||
0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x85, 0x02, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00,
|
||||
0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x03, 0x95, 0x06,
|
||||
0x75, 0x08, 0x15, 0x00, 0x25, 0x73, 0x05, 0x07, 0x19, 0x00, 0x29, 0x73, 0x81, 0x00, 0xc0, 0x05,
|
||||
0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x85, 0x01, 0x05, 0x09, 0x19, 0x01, 0x29,
|
||||
0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81,
|
||||
0x03, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95,
|
||||
0x03, 0x81, 0x06, 0xc0, 0xc0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var descriptorCDCMIDI = USBDescriptor{
|
||||
Device: []byte{
|
||||
0x12, 0x01, 0x00, 0x02, 0xef, 0x02, 0x01, 0x40, 0x86, 0x28, 0x2d, 0x80, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01,
|
||||
},
|
||||
Configuration: []byte{
|
||||
0x09, 0x02, 0xaf, 0x00, 0x04, 0x01, 0x00, 0xa0, 0x32,
|
||||
0x08, 0x0b, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,
|
||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00,
|
||||
0x05, 0x24, 0x00, 0x10, 0x01,
|
||||
0x04, 0x24, 0x02, 0x06,
|
||||
0x05, 0x24, 0x06, 0x00, 0x01,
|
||||
0x05, 0x24, 0x01, 0x01, 0x01,
|
||||
0x07, 0x05, 0x81, 0x03, 0x10, 0x00, 0x10,
|
||||
0x09, 0x04, 0x01, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
|
||||
0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00,
|
||||
0x08, 0x0b, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00,
|
||||
0x09, 0x04, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
|
||||
0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x03,
|
||||
0x09, 0x04, 0x03, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00,
|
||||
0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00,
|
||||
0x06, 0x24, 0x02, 0x01, 0x01, 0x00,
|
||||
0x06, 0x24, 0x02, 0x02, 0x02, 0x00,
|
||||
0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00,
|
||||
0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x01, 0x01, 0x00,
|
||||
0x09, 0x05, 0x05, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||
0x05, 0x25, 0x01, 0x01, 0x01,
|
||||
0x09, 0x05, 0x86, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||
0x05, 0x25, 0x01, 0x01, 0x03,
|
||||
},
|
||||
}
|
||||
+28
-2
@@ -38,9 +38,26 @@ func NewFile(fd uintptr, name string) *File {
|
||||
return &File{&file{stdioFileHandle(fd), name}}
|
||||
}
|
||||
|
||||
// Read is unsupported on this system.
|
||||
// Read reads up to len(b) bytes from machine.Serial.
|
||||
// It returns the number of bytes read and any error encountered.
|
||||
func (f stdioFileHandle) Read(b []byte) (n int, err error) {
|
||||
return 0, ErrUnsupported
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
size := buffered()
|
||||
for size == 0 {
|
||||
gosched()
|
||||
size = buffered()
|
||||
}
|
||||
|
||||
if size > len(b) {
|
||||
size = len(b)
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
b[i] = getchar()
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func (f stdioFileHandle) ReadAt(b []byte, off int64) (n int, err error) {
|
||||
@@ -78,6 +95,15 @@ func (f stdioFileHandle) Fd() uintptr {
|
||||
//go:linkname putchar runtime.putchar
|
||||
func putchar(c byte)
|
||||
|
||||
//go:linkname getchar runtime.getchar
|
||||
func getchar() byte
|
||||
|
||||
//go:linkname buffered runtime.buffered
|
||||
func buffered() int
|
||||
|
||||
//go:linkname gosched runtime.Gosched
|
||||
func gosched() int
|
||||
|
||||
func Pipe() (r *File, w *File, err error) {
|
||||
return nil, nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build windows || darwin || (linux && !baremetal)
|
||||
// +build windows darwin linux,!baremetal
|
||||
|
||||
package os_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetpagesize(t *testing.T) {
|
||||
pagesize := os.Getpagesize()
|
||||
if pagesize == 0x1000 || pagesize == 0x4000 || pagesize == 0x10000 {
|
||||
return
|
||||
}
|
||||
t.Errorf("os.Getpagesize() returns strange value %d", pagesize)
|
||||
}
|
||||
@@ -7,6 +7,11 @@
|
||||
|
||||
package os
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Getpagesize returns the underlying system's memory page size.
|
||||
func Getpagesize() int { return syscall.Getpagesize() }
|
||||
|
||||
func (fs *fileStat) Name() string { return fs.name }
|
||||
func (fs *fileStat) IsDir() bool { return fs.Mode().IsDir() }
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]struct{}) bool {
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
if v1.UnsafePointer() == v2.UnsafePointer() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
@@ -91,7 +91,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]struct{}) bool {
|
||||
}
|
||||
return deepValueEqual(v1.Elem(), v2.Elem(), visited)
|
||||
case Ptr:
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
if v1.UnsafePointer() == v2.UnsafePointer() {
|
||||
return true
|
||||
}
|
||||
return deepValueEqual(v1.Elem(), v2.Elem(), visited)
|
||||
@@ -109,7 +109,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]struct{}) bool {
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
if v1.UnsafePointer() == v2.UnsafePointer() {
|
||||
return true
|
||||
}
|
||||
for _, k := range v1.MapKeys() {
|
||||
|
||||
+10
-4
@@ -135,16 +135,22 @@ func (v Value) IsNil() bool {
|
||||
// Pointer returns the underlying pointer of the given value for the following
|
||||
// types: chan, map, pointer, unsafe.Pointer, slice, func.
|
||||
func (v Value) Pointer() uintptr {
|
||||
return uintptr(v.UnsafePointer())
|
||||
}
|
||||
|
||||
// UnsafePointer returns the underlying pointer of the given value for the
|
||||
// following types: chan, map, pointer, unsafe.Pointer, slice, func.
|
||||
func (v Value) UnsafePointer() unsafe.Pointer {
|
||||
switch v.Kind() {
|
||||
case Chan, Map, Ptr, UnsafePointer:
|
||||
return uintptr(v.pointer())
|
||||
return v.pointer()
|
||||
case Slice:
|
||||
slice := (*sliceHeader)(v.value)
|
||||
return uintptr(slice.data)
|
||||
return slice.data
|
||||
case Func:
|
||||
panic("unimplemented: (reflect.Value).Pointer()")
|
||||
panic("unimplemented: (reflect.Value).UnsafePointer()")
|
||||
default: // not implemented: Func
|
||||
panic(&ValueError{Method: "Pointer"})
|
||||
panic(&ValueError{Method: "UnsafePointer"})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,16 +26,16 @@ func xorshift32(x uint32) uint32 {
|
||||
// This function is used by hash/maphash.
|
||||
func memhash(p unsafe.Pointer, seed, s uintptr) uintptr {
|
||||
if unsafe.Sizeof(uintptr(0)) > 4 {
|
||||
return seed ^ uintptr(hash64(p, s))
|
||||
return uintptr(hash64(p, s, seed))
|
||||
}
|
||||
return seed ^ uintptr(hash32(p, s))
|
||||
return uintptr(hash32(p, s, seed))
|
||||
}
|
||||
|
||||
// Get FNV-1a hash of the given memory buffer.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
|
||||
func hash32(ptr unsafe.Pointer, n uintptr) uint32 {
|
||||
var result uint32 = 2166136261 // FNV offset basis
|
||||
func hash32(ptr unsafe.Pointer, n uintptr, seed uintptr) uint32 {
|
||||
var result uint32 = 2166136261 ^ uint32(seed) // FNV offset basis
|
||||
for i := uintptr(0); i < n; i++ {
|
||||
c := *(*uint8)(unsafe.Pointer(uintptr(ptr) + i))
|
||||
result ^= uint32(c) // XOR with byte
|
||||
@@ -45,8 +45,8 @@ func hash32(ptr unsafe.Pointer, n uintptr) uint32 {
|
||||
}
|
||||
|
||||
// Also a FNV-1a hash.
|
||||
func hash64(ptr unsafe.Pointer, n uintptr) uint64 {
|
||||
var result uint64 = 14695981039346656037 // FNV offset basis
|
||||
func hash64(ptr unsafe.Pointer, n uintptr, seed uintptr) uint64 {
|
||||
var result uint64 = 14695981039346656037 ^ uint64(seed) // FNV offset basis
|
||||
for i := uintptr(0); i < n; i++ {
|
||||
c := *(*uint8)(unsafe.Pointer(uintptr(ptr) + i))
|
||||
result ^= uint64(c) // XOR with byte
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
package runtime
|
||||
|
||||
// This file implements markGlobals for all the files that don't have a more
|
||||
// specific implementation.
|
||||
|
||||
// markGlobals marks all globals, which are reachable by definition.
|
||||
//
|
||||
// This implementation marks all globals conservatively and assumes it can use
|
||||
@@ -1,35 +0,0 @@
|
||||
//go:build gc.conservative && !baremetal && !darwin && !nintendoswitch && !tinygo.wasm && !windows
|
||||
// +build gc.conservative,!baremetal,!darwin,!nintendoswitch,!tinygo.wasm,!windows
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:extern runtime.trackedGlobalsStart
|
||||
var trackedGlobalsStart uintptr
|
||||
|
||||
//go:extern runtime.trackedGlobalsLength
|
||||
var trackedGlobalsLength uintptr
|
||||
|
||||
//go:extern runtime.trackedGlobalsBitmap
|
||||
var trackedGlobalsBitmap [0]uint8
|
||||
|
||||
// markGlobals marks all globals, which are reachable by definition.
|
||||
//
|
||||
// This implementation relies on a compiler pass that stores all globals in a
|
||||
// single global (adjusting all uses of them accordingly) and creates a bit
|
||||
// vector with the locations of each pointer. This implementation then walks the
|
||||
// bit vector and for each pointer it indicates, it marks the root.
|
||||
//
|
||||
//go:nobounds
|
||||
func markGlobals() {
|
||||
for i := uintptr(0); i < trackedGlobalsLength; i++ {
|
||||
if trackedGlobalsBitmap[i/8]&(1<<(i%8)) != 0 {
|
||||
addr := trackedGlobalsStart + i*unsafe.Alignof(uintptr(0))
|
||||
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||
markRoot(addr, root)
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
-39
@@ -13,12 +13,13 @@ import (
|
||||
// The underlying hashmap structure for Go.
|
||||
type hashmap struct {
|
||||
buckets unsafe.Pointer // pointer to array of buckets
|
||||
seed uintptr
|
||||
count uintptr
|
||||
keySize uint8 // maybe this can store the key type as well? E.g. keysize == 5 means string?
|
||||
valueSize uint8
|
||||
bucketBits uint8
|
||||
keyEqual func(x, y unsafe.Pointer, n uintptr) bool
|
||||
keyHash func(key unsafe.Pointer, size uintptr) uint32
|
||||
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32
|
||||
}
|
||||
|
||||
type hashmapAlgorithm uint8
|
||||
@@ -74,6 +75,7 @@ func hashmapMake(keySize, valueSize uint8, sizeHint uintptr, alg uint8) *hashmap
|
||||
|
||||
return &hashmap{
|
||||
buckets: buckets,
|
||||
seed: uintptr(fastrand()),
|
||||
keySize: keySize,
|
||||
valueSize: valueSize,
|
||||
bucketBits: bucketBits,
|
||||
@@ -96,7 +98,7 @@ func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintpt
|
||||
}
|
||||
}
|
||||
|
||||
func hashmapKeyHashAlg(alg hashmapAlgorithm) func(key unsafe.Pointer, n uintptr) uint32 {
|
||||
func hashmapKeyHashAlg(alg hashmapAlgorithm) func(key unsafe.Pointer, n, seed uintptr) uint32 {
|
||||
switch alg {
|
||||
case hashmapAlgorithmBinary:
|
||||
return hash32
|
||||
@@ -148,12 +150,14 @@ func hashmapLenUnsafePointer(p unsafe.Pointer) int {
|
||||
// Set a specified key to a given value. Grow the map if necessary.
|
||||
//go:nobounds
|
||||
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32) {
|
||||
tophash := hashmapTopHash(hash)
|
||||
|
||||
if hashmapShouldGrow(m) {
|
||||
hashmapGrow(m)
|
||||
// seed changed when we grew; rehash key with new seed
|
||||
hash = m.keyHash(key, uintptr(m.keySize), m.seed)
|
||||
}
|
||||
|
||||
tophash := hashmapTopHash(hash)
|
||||
|
||||
numBuckets := uintptr(1) << m.bucketBits
|
||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||
@@ -221,10 +225,10 @@ func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash u
|
||||
}
|
||||
|
||||
func hashmapGrow(m *hashmap) {
|
||||
|
||||
// clone map as empty
|
||||
n := *m
|
||||
n.count = 0
|
||||
n.seed = uintptr(fastrand())
|
||||
|
||||
// allocate our new buckets twice as big
|
||||
n.bucketBits = m.bucketBits + 1
|
||||
@@ -239,7 +243,7 @@ func hashmapGrow(m *hashmap) {
|
||||
var value = alloc(uintptr(m.valueSize), nil)
|
||||
|
||||
for hashmapNext(m, &it, key, value) {
|
||||
h := m.keyHash(key, uintptr(m.keySize))
|
||||
h := n.keyHash(key, uintptr(n.keySize), n.seed)
|
||||
hashmapSet(&n, key, value, h)
|
||||
}
|
||||
|
||||
@@ -386,7 +390,7 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
|
||||
|
||||
// Our view of the buckets doesn't match the parent map.
|
||||
// Look up the key in the new buckets and return that value if it exists
|
||||
hash := m.keyHash(key, uintptr(m.keySize))
|
||||
hash := m.keyHash(key, uintptr(m.keySize), m.seed)
|
||||
ok := hashmapGet(m, key, value, uintptr(m.valueSize), hash)
|
||||
if !ok {
|
||||
// doesn't exist in parent map; try next key
|
||||
@@ -401,10 +405,11 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
|
||||
}
|
||||
|
||||
// Hashmap with plain binary data keys (not containing strings etc.).
|
||||
|
||||
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
||||
// TODO: detect nil map here and throw a better panic message?
|
||||
hash := hash32(key, uintptr(m.keySize))
|
||||
if m == nil {
|
||||
nilMapPanic()
|
||||
}
|
||||
hash := hash32(key, uintptr(m.keySize), m.seed)
|
||||
hashmapSet(m, key, value, hash)
|
||||
}
|
||||
|
||||
@@ -413,7 +418,7 @@ func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr)
|
||||
memzero(value, uintptr(valueSize))
|
||||
return false
|
||||
}
|
||||
hash := hash32(key, uintptr(m.keySize))
|
||||
hash := hash32(key, uintptr(m.keySize), m.seed)
|
||||
return hashmapGet(m, key, value, valueSize, hash)
|
||||
}
|
||||
|
||||
@@ -421,7 +426,7 @@ func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
hash := hash32(key, uintptr(m.keySize))
|
||||
hash := hash32(key, uintptr(m.keySize), m.seed)
|
||||
hashmapDelete(m, key, hash)
|
||||
}
|
||||
|
||||
@@ -431,28 +436,38 @@ func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||
return *(*string)(x) == *(*string)(y)
|
||||
}
|
||||
|
||||
func hashmapStringHash(s string) uint32 {
|
||||
func hashmapStringHash(s string, seed uintptr) uint32 {
|
||||
_s := (*_string)(unsafe.Pointer(&s))
|
||||
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length))
|
||||
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length), seed)
|
||||
}
|
||||
|
||||
func hashmapStringPtrHash(sptr unsafe.Pointer, size uintptr) uint32 {
|
||||
func hashmapStringPtrHash(sptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
|
||||
_s := *(*_string)(sptr)
|
||||
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length))
|
||||
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length), seed)
|
||||
}
|
||||
|
||||
func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
hash := hashmapStringHash(key)
|
||||
if m == nil {
|
||||
nilMapPanic()
|
||||
}
|
||||
hash := hashmapStringHash(key, m.seed)
|
||||
hashmapSet(m, unsafe.Pointer(&key), value, hash)
|
||||
}
|
||||
|
||||
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool {
|
||||
hash := hashmapStringHash(key)
|
||||
if m == nil {
|
||||
memzero(value, uintptr(valueSize))
|
||||
return false
|
||||
}
|
||||
hash := hashmapStringHash(key, m.seed)
|
||||
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
|
||||
}
|
||||
|
||||
func hashmapStringDelete(m *hashmap, key string) {
|
||||
hash := hashmapStringHash(key)
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
hash := hashmapStringHash(key, m.seed)
|
||||
hashmapDelete(m, unsafe.Pointer(&key), hash)
|
||||
}
|
||||
|
||||
@@ -465,25 +480,25 @@ func hashmapStringDelete(m *hashmap, key string) {
|
||||
//go:linkname valueInterfaceUnsafe reflect.valueInterfaceUnsafe
|
||||
func valueInterfaceUnsafe(v reflect.Value) interface{}
|
||||
|
||||
func hashmapFloat32Hash(ptr unsafe.Pointer) uint32 {
|
||||
func hashmapFloat32Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
|
||||
f := *(*uint32)(ptr)
|
||||
if f == 0x80000000 {
|
||||
// convert -0 to 0 for hashing
|
||||
f = 0
|
||||
}
|
||||
return hash32(unsafe.Pointer(&f), 4)
|
||||
return hash32(unsafe.Pointer(&f), 4, seed)
|
||||
}
|
||||
|
||||
func hashmapFloat64Hash(ptr unsafe.Pointer) uint32 {
|
||||
func hashmapFloat64Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
|
||||
f := *(*uint64)(ptr)
|
||||
if f == 0x8000000000000000 {
|
||||
// convert -0 to 0 for hashing
|
||||
f = 0
|
||||
}
|
||||
return hash32(unsafe.Pointer(&f), 8)
|
||||
return hash32(unsafe.Pointer(&f), 8, seed)
|
||||
}
|
||||
|
||||
func hashmapInterfaceHash(itf interface{}) uint32 {
|
||||
func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
|
||||
x := reflect.ValueOf(itf)
|
||||
if x.RawType() == 0 {
|
||||
return 0 // nil interface
|
||||
@@ -498,41 +513,41 @@ func hashmapInterfaceHash(itf interface{}) uint32 {
|
||||
|
||||
switch x.RawType().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return hash32(ptr, x.RawType().Size())
|
||||
return hash32(ptr, x.RawType().Size(), seed)
|
||||
case reflect.Bool, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return hash32(ptr, x.RawType().Size())
|
||||
return hash32(ptr, x.RawType().Size(), seed)
|
||||
case reflect.Float32:
|
||||
// It should be possible to just has the contents. However, NaN != NaN
|
||||
// so if you're using lots of NaNs as map keys (you shouldn't) then hash
|
||||
// time may become exponential. To fix that, it would be better to
|
||||
// return a random number instead:
|
||||
// https://research.swtch.com/randhash
|
||||
return hashmapFloat32Hash(ptr)
|
||||
return hashmapFloat32Hash(ptr, seed)
|
||||
case reflect.Float64:
|
||||
return hashmapFloat64Hash(ptr)
|
||||
return hashmapFloat64Hash(ptr, seed)
|
||||
case reflect.Complex64:
|
||||
rptr, iptr := ptr, unsafe.Pointer(uintptr(ptr)+4)
|
||||
return hashmapFloat32Hash(rptr) ^ hashmapFloat32Hash(iptr)
|
||||
return hashmapFloat32Hash(rptr, seed) ^ hashmapFloat32Hash(iptr, seed)
|
||||
case reflect.Complex128:
|
||||
rptr, iptr := ptr, unsafe.Pointer(uintptr(ptr)+8)
|
||||
return hashmapFloat64Hash(rptr) ^ hashmapFloat64Hash(iptr)
|
||||
return hashmapFloat64Hash(rptr, seed) ^ hashmapFloat64Hash(iptr, seed)
|
||||
case reflect.String:
|
||||
return hashmapStringHash(x.String())
|
||||
return hashmapStringHash(x.String(), seed)
|
||||
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
|
||||
// It might seem better to just return the pointer, but that won't
|
||||
// result in an evenly distributed hashmap. Instead, hash the pointer
|
||||
// like most other types.
|
||||
return hash32(ptr, x.RawType().Size())
|
||||
return hash32(ptr, x.RawType().Size(), seed)
|
||||
case reflect.Array:
|
||||
var hash uint32
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)))
|
||||
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)), seed)
|
||||
}
|
||||
return hash
|
||||
case reflect.Struct:
|
||||
var hash uint32
|
||||
for i := 0; i < x.NumField(); i++ {
|
||||
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)))
|
||||
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)), seed)
|
||||
}
|
||||
return hash
|
||||
default:
|
||||
@@ -541,9 +556,9 @@ func hashmapInterfaceHash(itf interface{}) uint32 {
|
||||
}
|
||||
}
|
||||
|
||||
func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr) uint32 {
|
||||
func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
|
||||
_i := *(*_interface)(iptr)
|
||||
return hashmapInterfaceHash(_i)
|
||||
return hashmapInterfaceHash(_i, seed)
|
||||
}
|
||||
|
||||
func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||
@@ -551,16 +566,26 @@ func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||
}
|
||||
|
||||
func hashmapInterfaceSet(m *hashmap, key interface{}, value unsafe.Pointer) {
|
||||
hash := hashmapInterfaceHash(key)
|
||||
if m == nil {
|
||||
nilMapPanic()
|
||||
}
|
||||
hash := hashmapInterfaceHash(key, m.seed)
|
||||
hashmapSet(m, unsafe.Pointer(&key), value, hash)
|
||||
}
|
||||
|
||||
func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer, valueSize uintptr) bool {
|
||||
hash := hashmapInterfaceHash(key)
|
||||
if m == nil {
|
||||
memzero(value, uintptr(valueSize))
|
||||
return false
|
||||
}
|
||||
hash := hashmapInterfaceHash(key, m.seed)
|
||||
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
|
||||
}
|
||||
|
||||
func hashmapInterfaceDelete(m *hashmap, key interface{}) {
|
||||
hash := hashmapInterfaceHash(key)
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
hash := hashmapInterfaceHash(key, m.seed)
|
||||
hashmapDelete(m, unsafe.Pointer(&key), hash)
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func reflectValueEqual(x, y reflect.Value) bool {
|
||||
case reflect.String:
|
||||
return x.String() == y.String()
|
||||
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
|
||||
return x.Pointer() == y.Pointer()
|
||||
return x.UnsafePointer() == y.UnsafePointer()
|
||||
case reflect.Array:
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
if !reflectValueEqual(x.Index(i), y.Index(i)) {
|
||||
|
||||
+32
-2
@@ -1,8 +1,13 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
//go:build linux && !baremetal && !nintendoswitch && !wasi
|
||||
// +build linux,!baremetal,!nintendoswitch,!wasi
|
||||
|
||||
package runtime
|
||||
|
||||
// This file is for systems that are _actually_ Linux (not systems that pretend
|
||||
// to be Linux, like baremetal systems).
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const GOOS = "linux"
|
||||
|
||||
const (
|
||||
@@ -18,3 +23,28 @@ const (
|
||||
clock_REALTIME = 0
|
||||
clock_MONOTONIC_RAW = 4
|
||||
)
|
||||
|
||||
//go:extern _edata
|
||||
var globalsStartSymbol [0]byte
|
||||
|
||||
//go:extern _end
|
||||
var globalsEndSymbol [0]byte
|
||||
|
||||
// markGlobals marks all globals, which are reachable by definition.
|
||||
//
|
||||
// This implementation marks all globals conservatively and assumes it can use
|
||||
// linker-defined symbols for the start and end of the .data section.
|
||||
func markGlobals() {
|
||||
start := uintptr(unsafe.Pointer(&globalsStartSymbol))
|
||||
end := uintptr(unsafe.Pointer(&globalsEndSymbol))
|
||||
start = (start + unsafe.Alignof(uintptr(0)) - 1) &^ (unsafe.Alignof(uintptr(0)) - 1) // align on word boundary
|
||||
markRoots(start, end)
|
||||
}
|
||||
|
||||
//export getpagesize
|
||||
func libc_getpagesize() int
|
||||
|
||||
//go:linkname syscall_Getpagesize syscall.Getpagesize
|
||||
func syscall_Getpagesize() int {
|
||||
return libc_getpagesize()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build linux && (baremetal || nintendoswitch || wasi)
|
||||
// +build linux
|
||||
// +build baremetal nintendoswitch wasi
|
||||
|
||||
// Other systems that aren't operating systems supported by the Go toolchain
|
||||
// need to pretend to be an existing operating system. Linux seems like a good
|
||||
// choice for this for its wide hardware support.
|
||||
|
||||
package runtime
|
||||
|
||||
const GOOS = "linux"
|
||||
@@ -90,3 +90,26 @@ func markGlobals() {
|
||||
section = (*peSection)(unsafe.Pointer(uintptr(unsafe.Pointer(section)) + unsafe.Sizeof(peSection{})))
|
||||
}
|
||||
}
|
||||
|
||||
type systeminfo struct {
|
||||
anon0 [4]byte
|
||||
dwpagesize uint32
|
||||
lpminimumapplicationaddress *byte
|
||||
lpmaximumapplicationaddress *byte
|
||||
dwactiveprocessormask uintptr
|
||||
dwnumberofprocessors uint32
|
||||
dwprocessortype uint32
|
||||
dwallocationgranularity uint32
|
||||
wprocessorlevel uint16
|
||||
wprocessorrevision uint16
|
||||
}
|
||||
|
||||
//export GetSystemInfo
|
||||
func _GetSystemInfo(lpSystemInfo unsafe.Pointer)
|
||||
|
||||
//go:linkname syscall_Getpagesize syscall.Getpagesize
|
||||
func syscall_Getpagesize() int {
|
||||
var info systeminfo
|
||||
_GetSystemInfo(unsafe.Pointer(&info))
|
||||
return int(info.dwpagesize)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ func nilPanic() {
|
||||
runtimePanic("nil pointer dereference")
|
||||
}
|
||||
|
||||
// Panic when trying to add an entry to a nil map
|
||||
func nilMapPanic() {
|
||||
runtimePanic("assignment to entry in nil map")
|
||||
}
|
||||
|
||||
// Panic when trying to acces an array or slice out of bounds.
|
||||
func lookupPanic() {
|
||||
runtimePanic("index out of range")
|
||||
|
||||
@@ -14,6 +14,16 @@ func putchar(c byte) {
|
||||
// dummy, TODO
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
// dummy, TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
// dummy, TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:extern _sbss
|
||||
var _sbss [0]byte
|
||||
|
||||
|
||||
@@ -16,6 +16,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
// Sleep for a given period. The period is defined by the WDT peripheral, and is
|
||||
// on most chips (at least) 3 bits wide, in powers of two from 16ms to 2s
|
||||
// (0=16ms, 1=32ms, 2=64ms...). Note that the WDT is not very accurate: it can
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"machine"
|
||||
"machine/usb/cdc"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
@@ -29,16 +30,27 @@ func init() {
|
||||
initADCClock()
|
||||
|
||||
// connect to USB CDC interface
|
||||
cdc.EnableUSBCDC()
|
||||
machine.USB.Configure(machine.UARTConfig{})
|
||||
machine.Serial.Configure(machine.UARTConfig{})
|
||||
if !machine.USB.Configured() {
|
||||
machine.USB.Configure(machine.UARTConfig{})
|
||||
}
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
func initClocks() {
|
||||
// Set 1 Flash Wait State for 48MHz, required for 3.3V operation according to SAMD21 Datasheet
|
||||
sam.NVMCTRL.CTRLB.SetBits(sam.NVMCTRL_CTRLB_RWS_HALF << sam.NVMCTRL_CTRLB_RWS_Pos)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"machine"
|
||||
"machine/usb/cdc"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
@@ -29,16 +30,27 @@ func init() {
|
||||
initADCClock()
|
||||
|
||||
// connect to USB CDC interface
|
||||
cdc.EnableUSBCDC()
|
||||
machine.USB.Configure(machine.UARTConfig{})
|
||||
machine.Serial.Configure(machine.UARTConfig{})
|
||||
if !machine.USB.Configured() {
|
||||
machine.USB.Configure(machine.UARTConfig{})
|
||||
}
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
func initClocks() {
|
||||
// set flash wait state
|
||||
sam.NVMCTRL.CTRLA.SetBits(0 << sam.NVMCTRL_CTRLA_RWS_Pos)
|
||||
|
||||
@@ -14,6 +14,16 @@ func putchar(c byte) {
|
||||
// UART is not supported.
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
// UART is not supported.
|
||||
return 0
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
// UART is not supported.
|
||||
return 0
|
||||
}
|
||||
|
||||
func sleepWDT(period uint8) {
|
||||
// TODO: use the watchdog timer instead of a busy loop.
|
||||
for i := 0x45; i != 0; i-- {
|
||||
|
||||
@@ -49,6 +49,16 @@ func putchar(c byte) {
|
||||
stdoutWrite.Set(uint8(c))
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
// dummy, TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
// dummy, TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
func waitForEvents() {
|
||||
arm.Asm("wfe")
|
||||
}
|
||||
|
||||
@@ -15,6 +15,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
// Initialize .bss: zero-initialized global variables.
|
||||
// The .data section has already been loaded by the ROM bootloader.
|
||||
func clearbss() {
|
||||
|
||||
@@ -18,6 +18,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
// Write to the internal control bus (using I2C?).
|
||||
// Signature found here:
|
||||
// https://github.com/espressif/ESP8266_RTOS_SDK/blob/14171de0/components/esp8266/include/esp8266/rom_functions.h#L54
|
||||
|
||||
@@ -99,6 +99,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
var timerWakeup volatile.Register8
|
||||
|
||||
func ticks() timeUnit {
|
||||
|
||||
@@ -111,6 +111,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
var timerWakeup volatile.Register8
|
||||
|
||||
func ticks() timeUnit {
|
||||
|
||||
@@ -126,6 +126,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.UART1.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.UART1.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.UART1.Buffered()
|
||||
}
|
||||
|
||||
func exit(code int) {
|
||||
abort()
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"device/arm"
|
||||
"device/nrf"
|
||||
"machine"
|
||||
"machine/usb/cdc"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
@@ -28,6 +29,9 @@ func main() {
|
||||
}
|
||||
|
||||
func init() {
|
||||
// connect to USB CDC interface
|
||||
cdc.EnableUSBCDC()
|
||||
machine.USB.Configure(machine.UARTConfig{})
|
||||
machine.Serial.Configure(machine.UARTConfig{})
|
||||
initLFCLK()
|
||||
initRTC()
|
||||
@@ -66,6 +70,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
func sleepTicks(d timeUnit) {
|
||||
for d != 0 {
|
||||
ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side)
|
||||
|
||||
@@ -231,6 +231,16 @@ func putchar(c byte) {
|
||||
machine.PutcharUART(machine.UART0, c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
// dummy, TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
// dummy, TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
func exit(code int) {
|
||||
abort()
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
|
||||
"machine"
|
||||
"machine/usb/cdc"
|
||||
)
|
||||
|
||||
// machineTicks is provided by package machine.
|
||||
@@ -58,12 +58,26 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
// machineInit is provided by package machine.
|
||||
func machineInit()
|
||||
|
||||
func init() {
|
||||
machineInit()
|
||||
|
||||
cdc.EnableUSBCDC()
|
||||
machine.USB.Configure(machine.UARTConfig{})
|
||||
machine.Serial.Configure(machine.UARTConfig{})
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
// initCLK sets clock to 72MHz using HSE 8MHz crystal w/ PLL X 9 (8MHz x 9 = 72MHz).
|
||||
func initCLK() {
|
||||
stm32.FLASH.ACR.SetBits(stm32.FLASH_ACR_LATENCY_WS2) // Two wait states, per datasheet
|
||||
|
||||
@@ -21,6 +21,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
func initCLK() {
|
||||
// Reset clock registers
|
||||
// Set HSION
|
||||
|
||||
@@ -163,3 +163,15 @@ func initCOM() {
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
@@ -38,6 +38,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
func initCLK() {
|
||||
// PWR_CLK_ENABLE
|
||||
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_PWREN)
|
||||
|
||||
@@ -16,6 +16,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
func initCLK() {
|
||||
// Set Power Regulator to enable max performance (1.8V)
|
||||
stm32.PWR.CR.ReplaceBits(1<<stm32.PWR_CR_VOS_Pos, stm32.PWR_CR_VOS_Msk, 0)
|
||||
|
||||
@@ -47,6 +47,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
func initCLK() {
|
||||
// PWR_CLK_ENABLE
|
||||
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_PWREN)
|
||||
|
||||
@@ -39,6 +39,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
func initCLK() {
|
||||
|
||||
// PWR_CLK_ENABLE
|
||||
|
||||
@@ -102,3 +102,15 @@ func initCLK() {
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
@@ -55,6 +55,16 @@ func putchar(c byte) {
|
||||
stdoutWrite.Set(uint8(c))
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
// dummy, TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
// dummy, TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
func abort() {
|
||||
exit(1)
|
||||
}
|
||||
|
||||
@@ -251,10 +251,6 @@ func Mprotect(b []byte, prot int) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func Getpagesize() int {
|
||||
return int(libc_getpagesize())
|
||||
}
|
||||
|
||||
func Environ() []string {
|
||||
|
||||
// This function combines all the environment into a single allocation.
|
||||
@@ -372,10 +368,6 @@ func libc_munmap(addr unsafe.Pointer, length uintptr) int32
|
||||
//export mprotect
|
||||
func libc_mprotect(addr unsafe.Pointer, len uintptr, prot int32) int32
|
||||
|
||||
// int getpagesize();
|
||||
//export getpagesize
|
||||
func libc_getpagesize() int32
|
||||
|
||||
// int chdir(const char *pathname, mode_t mode);
|
||||
//export chdir
|
||||
func libc_chdir(pathname *byte) int32
|
||||
|
||||
@@ -267,6 +267,14 @@ func readdir_r(dir uintptr, entry *Dirent, result **Dirent) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func Getpagesize() int {
|
||||
return int(libc_getpagesize())
|
||||
}
|
||||
|
||||
// int pipe(int32 *fds);
|
||||
//export pipe
|
||||
func libc_pipe(fds *int32) int32
|
||||
|
||||
// int getpagesize();
|
||||
//export getpagesize
|
||||
func libc_getpagesize() int32
|
||||
|
||||
@@ -67,3 +67,7 @@ func getErrno() error {
|
||||
func Pipe2(p []int, flags int) (err error) {
|
||||
return ENOSYS // TODO
|
||||
}
|
||||
|
||||
func Getpagesize() int {
|
||||
return 4096 // TODO
|
||||
}
|
||||
|
||||
@@ -296,6 +296,11 @@ func Pipe2(p []int, flags int) (err error) {
|
||||
return ENOSYS // TODO
|
||||
}
|
||||
|
||||
func Getpagesize() int {
|
||||
// per upstream
|
||||
return 65536
|
||||
}
|
||||
|
||||
// int stat(const char *path, struct stat * buf);
|
||||
//export stat
|
||||
func libc_stat(pathname *byte, ptr unsafe.Pointer) int32
|
||||
|
||||
@@ -205,3 +205,9 @@ type Timeval struct {
|
||||
Sec int64
|
||||
Usec int64
|
||||
}
|
||||
|
||||
func Getpagesize() int {
|
||||
// There is no right value to return here, but 4096 is a
|
||||
// common assumption when pagesize is unknown
|
||||
return 4096
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"inherits": [
|
||||
"rp2040"
|
||||
],
|
||||
"serial": "uart",
|
||||
"serial-port": ["acm:239a:80f1"],
|
||||
"build-tags": ["feather_rp2040"],
|
||||
"linkerscript": "targets/feather-rp2040.ld",
|
||||
"extra-files": [
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"rp2040"
|
||||
],
|
||||
"build-tags": ["pico"],
|
||||
"serial-port": ["acm:2e8a:0003"],
|
||||
"serial": "uart",
|
||||
"linkerscript": "targets/pico.ld",
|
||||
"extra-files": [
|
||||
|
||||
Vendored
+41
-41
@@ -129,10 +129,10 @@
|
||||
- pkg: tea
|
||||
- pkg: twofish
|
||||
- pkg: xtea
|
||||
# chacha20 -- panic: chacha20: SetCounter attempted to rollback counter
|
||||
# cryptobyte -- panic: unimplemented: reflect.OverflowInt()
|
||||
# salsa20/salsa -- panic: runtime error: index out of range
|
||||
# sha3 -- panic: unimplemented: (reflect.Type).NumMethod()
|
||||
#- pkg: chacha20 # panic: chacha20: SetCounter attempted to rollback counter
|
||||
#- pkg: cryptobyte # panic: unimplemented: reflect.OverflowInt()
|
||||
#- pkg: salsa20/salsa # panic: runtime error: index out of range
|
||||
#- pkg: sha3 # panic: unimplemented: (reflect.Type).NumMethod()
|
||||
- repo: github.com/google/shlex
|
||||
- repo: github.com/google/btree
|
||||
- repo: github.com/google/der-ascii
|
||||
@@ -167,15 +167,15 @@
|
||||
- pkg: search
|
||||
- pkg: unicode/rangetable
|
||||
- pkg: message/catalog
|
||||
# 'collate/build', # -- panic: (reflect.Value).Interface: unexported
|
||||
# 'feature/plural', # TestSelect, TestOrdinal, TestCardinal fail
|
||||
# 'internal/catmsg', # TestCodec fails
|
||||
# 'internal/gen/bitfield', # panic: unimplemented: (reflect.Type).Name()
|
||||
# 'number', # fails due to printf %T formatting
|
||||
#- pkg: collate/build # panic: (reflect.Value).Interface: unexported
|
||||
#- pkg: feature/plural # TestSelect, TestOrdinal, TestCardinal fail
|
||||
#- pkg: internal/catmsg # TestCodec fails
|
||||
#- pkg: internal/gen/bitfield # panic: unimplemented: (reflect.Type).Name()
|
||||
#- pkg: number # fails due to printf %T formatting
|
||||
- repo: golang.org/x/image
|
||||
tags: noasm
|
||||
subdirs:
|
||||
# - bmp: needs _time.startTimer
|
||||
#- pkg: bmp # needs _time.startTimer
|
||||
- pkg: ccitt
|
||||
- pkg: colornames
|
||||
- pkg: draw
|
||||
@@ -193,7 +193,7 @@
|
||||
- pkg: r2
|
||||
- pkg: r3
|
||||
- pkg: s1
|
||||
# 's2' -- reflect.DeepEqual() -> MapKeys
|
||||
#- pkg: s2 # reflect.DeepEqual() -> MapKeys
|
||||
- repo: github.com/golang/groupcache
|
||||
subdirs:
|
||||
- pkg: consistenthash
|
||||
@@ -236,40 +236,40 @@
|
||||
slow: true
|
||||
- pkg: stat/samplemv
|
||||
skipwasi: true # takes too long
|
||||
# 'blas/blas64', # -- TestDasum panic: blas: n < 0
|
||||
# 'blas/gonum', # -- panic: blas: n < 0
|
||||
# 'cmplxs', # -- TestAdd panic: cmplxs: slice lengths do not match
|
||||
# 'diff/fd', # -- panic: fd: slice length mismatch
|
||||
# 'floats', # -- panic: floats: destination slice length does not match input
|
||||
# 'graph', # ld.lld-11: -- error: undefined symbol: reflect.mapiterkey (among other reflect errors)
|
||||
# 'graph/topo', # -- Reflect: Same as above
|
||||
# 'internal/math32', # -- /usr/local/go/src/testing/quick/quick.go:273:11: fType.NumOut undefined (type reflect.Type has no field or method NumOut)
|
||||
# 'interp', # -- panic: interp: input slices have different lengths
|
||||
# 'mat', # -- panic: mat: row index out of range
|
||||
# 'num/dual', # -- TestFormat unexpected result for fmt.Sprintf("%#v", T{Real:1.1, Emag:2.1}): got:"T{Real:1.1, Emag:2.1}", want:"dual.Number{Real:1.1, Emag:2.1}" unexpected result for fmt.Sprintf("%#v", T{Real:-1.1, Emag:-2.1}): got:"T{Real:-1.1, Emag:-2.1}", want:"dual.Number{Real:-1.1, Emag:-2.1}"
|
||||
# 'num/dualcmplx', # -- TestFormat (similar to above)
|
||||
# 'num/dualquat', # -- TestFormat (similar to above)
|
||||
# 'num/hyperdual', # -- TestFormat (similar to above)
|
||||
# 'num/quat', # -- TestFormat (similar to above)
|
||||
# 'optimize', # // ld.lld-11: error: undefined symbol: golang.org/x/tools/container/intsets.havePOPCNT error: failed to link ...
|
||||
# 'spatial/barneshut', # -- panic: unimplemented: (reflect.Value).MapKeys()
|
||||
# 'spatial/kdtree', # -- panic: unimplemented: (reflect.Value).MapKeys()
|
||||
# 'spatial/vptree', # -- panic: unimplemented: (reflect.Value).MapKeys()
|
||||
# 'stat', # -- panic: stat: slice length mismatch
|
||||
# 'stat/card', # -- /usr/local/go/src/encoding/gob/decode.go:562:21: MakeMapWithSize not declared by package reflect
|
||||
# 'stat/distuv', # -- panic: distuv: cannot compute Mode for Beta != 0\
|
||||
# 'stat/sampleuv', # -- TestWeightedTimeSeeded requires t.Skip(), otherwise passes
|
||||
# 'unit', # -- All Format tests fail. Similar to `num` subpackages.
|
||||
#- pkg: blas/blas64 # -- TestDasum panic: blas: n < 0
|
||||
#- pkg: blas/gonum # -- panic: blas: n < 0
|
||||
#- pkg: cmplxs # -- TestAdd panic: cmplxs: slice lengths do not match
|
||||
#- pkg: diff/fd # -- panic: fd: slice length mismatch
|
||||
#- pkg: floats # -- panic: floats: destination slice length does not match input
|
||||
#- pkg: graph # ld.lld-11: -- error: undefined symbol: reflect.mapiterkey (among other reflect errors)
|
||||
#- pkg: graph/topo # -- Reflect: Same as above
|
||||
#- pkg: internal/math32 # -- /usr/local/go/src/testing/quick/quick.go:273:11: fType.NumOut undefined (type reflect.Type has no field or method NumOut)
|
||||
#- pkg: interp # -- panic: interp: input slices have different lengths
|
||||
#- pkg: mat # -- panic: mat: row index out of range
|
||||
#- pkg: num/dual # TestFormat unexpected result for fmt.Sprintf("%#v", T{Real:1.1, Emag:2.1}): got:"T{Real:1.1, Emag:2.1}", want:"dual.Number{Real:1.1, Emag:2.1}" unexpected result for fmt.Sprintf("%#v", T{Real:-1.1, Emag:-2.1}): got:"T{Real:-1.1, Emag:-2.1}", want:"dual.Number{Real:-1.1, Emag:-2.1}"
|
||||
#- pkg: num/dualcmplx # TestFormat (similar to above)
|
||||
#- pkg: num/dualquat # TestFormat (similar to above)
|
||||
#- pkg: num/hyperdual # TestFormat (similar to above)
|
||||
#- pkg: num/quat # TestFormat (similar to above)
|
||||
#- pkg: optimize', # ld.lld-11: error: undefined symbol: golang.org/x/tools/container/intsets.havePOPCNT error: failed to link ...
|
||||
#- pkg: spatial/barneshut # panic: unimplemented: (reflect.Value).MapKeys()
|
||||
#- pkg: spatial/kdtree # panic: unimplemented: (reflect.Value).MapKeys()
|
||||
#- pkg: spatial/vptree # panic: unimplemented: (reflect.Value).MapKeys()
|
||||
#- pkg: stat # panic: stat: slice length mismatch
|
||||
#- pkg: stat/card # /usr/local/go/src/encoding/gob/decode.go:562:21: MakeMapWithSize not declared by package reflect
|
||||
#- pkg: stat/distuv # panic: distuv: cannot compute Mode for Beta != 0\
|
||||
#- pkg: stat/sampleuv # TestWeightedTimeSeeded requires t.Skip(), otherwise passes
|
||||
#- pkg: unit # All Format tests fail. Similar to `num` subpackages.
|
||||
|
||||
- repo: github.com/cloudflare/bm
|
||||
- repo: github.com/cloudflare/bn256
|
||||
tags: generic
|
||||
# "cloudflare/ahocorasick" -- interp timeout building regexps in test
|
||||
# - repo: github.com/google/open-location-code # unfortunately, Go discards the test files
|
||||
# version: master
|
||||
# skipwasi: true # needs file access
|
||||
# subdirs:
|
||||
# - pkg: go
|
||||
#- repo: cloudflare/ahocorasick # interp timeout building regexps in test
|
||||
#- repo: github.com/google/open-location-code # unfortunately, Go discards the test files
|
||||
# version: master
|
||||
# skipwasi: true # needs file access
|
||||
# subdirs:
|
||||
# - pkg: go
|
||||
- repo: github.com/chewxy/math32
|
||||
tags: noasm
|
||||
version: master
|
||||
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("add:", Add(3, 5))
|
||||
println("add:", Add(int8(3), 5))
|
||||
}
|
||||
|
||||
type Integer interface {
|
||||
int | int8 | int16 | int32 | int64
|
||||
}
|
||||
|
||||
func Add[T Integer](a, b T) T {
|
||||
return a + b
|
||||
}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
add: 8
|
||||
add: 8
|
||||
@@ -0,0 +1,6 @@
|
||||
package subdir
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSubdir(t *testing.T) {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package top
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestTop(t *testing.T) {
|
||||
}
|
||||
+10
-164
@@ -1,8 +1,6 @@
|
||||
package transform
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -273,37 +271,17 @@ func MakeGCStackSlots(mod llvm.Module) bool {
|
||||
// Make sure this stack object is popped from the linked list of stack
|
||||
// objects at return.
|
||||
for _, ret := range returns {
|
||||
inst := ret
|
||||
// Try to do the popping of the stack object earlier, by inserting
|
||||
// it not right before the return instruction but moving the insert
|
||||
// position up.
|
||||
// This is necessary so that the GC stack slot pass doesn't
|
||||
// interfere with tail calls (in particular, musttail calls).
|
||||
for {
|
||||
prevInst := llvm.PrevInstruction(inst)
|
||||
if prevInst == parent {
|
||||
break
|
||||
}
|
||||
if _, ok := pointerStores[prevInst]; ok {
|
||||
// Pop the stack object after the last store instruction.
|
||||
// This can probably be made more efficient: storing to the
|
||||
// stack chain object and then immediately popping isn't
|
||||
// useful.
|
||||
break
|
||||
}
|
||||
if prevInst.IsNil() {
|
||||
// Start of basic block. Pop the stack object here.
|
||||
break
|
||||
}
|
||||
if !prevInst.IsAPHINode().IsNil() {
|
||||
// Do not insert before a PHI node. PHI nodes must be
|
||||
// grouped at the beginning of a basic block before any
|
||||
// other instruction.
|
||||
break
|
||||
}
|
||||
inst = prevInst
|
||||
// Check for any tail calls at this return.
|
||||
prev := llvm.PrevInstruction(ret)
|
||||
if !prev.IsNil() && !prev.IsABitCastInst().IsNil() {
|
||||
// A bitcast can appear before a tail call, so skip backwards more.
|
||||
prev = llvm.PrevInstruction(prev)
|
||||
}
|
||||
builder.SetInsertPointBefore(inst)
|
||||
if !prev.IsNil() && !prev.IsACallInst().IsNil() {
|
||||
// This is no longer a tail call.
|
||||
prev.SetTailCall(false)
|
||||
}
|
||||
builder.SetInsertPointBefore(ret)
|
||||
builder.CreateStore(parent, stackChainStart)
|
||||
}
|
||||
}
|
||||
@@ -311,138 +289,6 @@ func MakeGCStackSlots(mod llvm.Module) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// AddGlobalsBitmap performs a few related functions. It is needed for scanning
|
||||
// globals on platforms where the .data/.bss section is not easily accessible by
|
||||
// the GC, and thus all globals that contain pointers must be made reachable by
|
||||
// the GC in some other way.
|
||||
//
|
||||
// First, it scans all globals, and bundles all globals that contain a pointer
|
||||
// into one large global (updating all uses in the process). Then it creates a
|
||||
// bitmap (bit vector) to locate all the pointers in this large global. This
|
||||
// bitmap allows the GC to know in advance where exactly all the pointers live
|
||||
// in the large globals bundle, to avoid false positives.
|
||||
func AddGlobalsBitmap(mod llvm.Module) bool {
|
||||
if mod.NamedGlobal("runtime.trackedGlobalsStart").IsNil() {
|
||||
return false // nothing to do: no GC in use
|
||||
}
|
||||
|
||||
ctx := mod.Context()
|
||||
targetData := llvm.NewTargetData(mod.DataLayout())
|
||||
defer targetData.Dispose()
|
||||
uintptrType := ctx.IntType(targetData.PointerSize() * 8)
|
||||
|
||||
// Collect all globals that contain pointers (and thus must be scanned by
|
||||
// the GC).
|
||||
var trackedGlobals []llvm.Value
|
||||
var trackedGlobalTypes []llvm.Type
|
||||
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
||||
if global.IsDeclaration() || global.IsGlobalConstant() {
|
||||
continue
|
||||
}
|
||||
typ := global.Type().ElementType()
|
||||
ptrs := getPointerBitmap(targetData, typ, global.Name())
|
||||
if ptrs.BitLen() == 0 {
|
||||
continue
|
||||
}
|
||||
trackedGlobals = append(trackedGlobals, global)
|
||||
trackedGlobalTypes = append(trackedGlobalTypes, typ)
|
||||
}
|
||||
|
||||
// Make a new global that bundles all existing globals, and remove the
|
||||
// existing globals. All uses of the previous independent globals are
|
||||
// replaced with a GEP into the new globals bundle.
|
||||
globalsBundleType := ctx.StructType(trackedGlobalTypes, false)
|
||||
globalsBundle := llvm.AddGlobal(mod, globalsBundleType, "tinygo.trackedGlobals")
|
||||
globalsBundle.SetLinkage(llvm.InternalLinkage)
|
||||
globalsBundle.SetUnnamedAddr(true)
|
||||
initializer := llvm.Undef(globalsBundleType)
|
||||
for i, global := range trackedGlobals {
|
||||
initializer = llvm.ConstInsertValue(initializer, global.Initializer(), []uint32{uint32(i)})
|
||||
gep := llvm.ConstGEP(globalsBundle, []llvm.Value{
|
||||
llvm.ConstInt(ctx.Int32Type(), 0, false),
|
||||
llvm.ConstInt(ctx.Int32Type(), uint64(i), false),
|
||||
})
|
||||
global.ReplaceAllUsesWith(gep)
|
||||
global.EraseFromParentAsGlobal()
|
||||
}
|
||||
globalsBundle.SetInitializer(initializer)
|
||||
|
||||
// Update trackedGlobalsStart, which points to the globals bundle.
|
||||
trackedGlobalsStart := llvm.ConstPtrToInt(globalsBundle, uintptrType)
|
||||
mod.NamedGlobal("runtime.trackedGlobalsStart").SetInitializer(trackedGlobalsStart)
|
||||
mod.NamedGlobal("runtime.trackedGlobalsStart").SetLinkage(llvm.InternalLinkage)
|
||||
|
||||
// Update trackedGlobalsLength, which contains the length (in words) of the
|
||||
// globals bundle.
|
||||
alignment := targetData.PrefTypeAlignment(llvm.PointerType(ctx.Int8Type(), 0))
|
||||
trackedGlobalsLength := llvm.ConstInt(uintptrType, targetData.TypeAllocSize(globalsBundleType)/uint64(alignment), false)
|
||||
mod.NamedGlobal("runtime.trackedGlobalsLength").SetLinkage(llvm.InternalLinkage)
|
||||
mod.NamedGlobal("runtime.trackedGlobalsLength").SetInitializer(trackedGlobalsLength)
|
||||
|
||||
// Create a bitmap (a new global) that stores for each word in the globals
|
||||
// bundle whether it contains a pointer. This allows globals to be scanned
|
||||
// precisely: no non-pointers will be considered pointers if the bit pattern
|
||||
// looks like one.
|
||||
// This code assumes that pointers are self-aligned. For example, that a
|
||||
// 32-bit (4-byte) pointer is also aligned to 4 bytes.
|
||||
bitmapBytes := getPointerBitmap(targetData, globalsBundleType, "globals bundle").Bytes()
|
||||
bitmapValues := make([]llvm.Value, len(bitmapBytes))
|
||||
for i, b := range bitmapBytes {
|
||||
bitmapValues[len(bitmapBytes)-i-1] = llvm.ConstInt(ctx.Int8Type(), uint64(b), false)
|
||||
}
|
||||
bitmapArray := llvm.ConstArray(ctx.Int8Type(), bitmapValues)
|
||||
bitmapNew := llvm.AddGlobal(mod, bitmapArray.Type(), "runtime.trackedGlobalsBitmap.tmp")
|
||||
bitmapOld := mod.NamedGlobal("runtime.trackedGlobalsBitmap")
|
||||
bitmapOld.ReplaceAllUsesWith(llvm.ConstBitCast(bitmapNew, bitmapOld.Type()))
|
||||
bitmapNew.SetInitializer(bitmapArray)
|
||||
bitmapNew.SetName("runtime.trackedGlobalsBitmap")
|
||||
bitmapNew.SetLinkage(llvm.InternalLinkage)
|
||||
|
||||
return true // the IR was changed
|
||||
}
|
||||
|
||||
// getPointerBitmap scans the given LLVM type for pointers and sets bits in a
|
||||
// bigint at the word offset that contains a pointer. This scan is recursive.
|
||||
func getPointerBitmap(targetData llvm.TargetData, typ llvm.Type, name string) *big.Int {
|
||||
alignment := targetData.PrefTypeAlignment(llvm.PointerType(typ.Context().Int8Type(), 0))
|
||||
switch typ.TypeKind() {
|
||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||
return big.NewInt(0)
|
||||
case llvm.PointerTypeKind:
|
||||
return big.NewInt(1)
|
||||
case llvm.StructTypeKind:
|
||||
ptrs := big.NewInt(0)
|
||||
for i, subtyp := range typ.StructElementTypes() {
|
||||
subptrs := getPointerBitmap(targetData, subtyp, name)
|
||||
if subptrs.BitLen() == 0 {
|
||||
continue
|
||||
}
|
||||
offset := targetData.ElementOffset(typ, i)
|
||||
if offset%uint64(alignment) != 0 {
|
||||
panic("precise GC: global contains unaligned pointer: " + name)
|
||||
}
|
||||
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
|
||||
ptrs.Or(ptrs, subptrs)
|
||||
}
|
||||
return ptrs
|
||||
case llvm.ArrayTypeKind:
|
||||
subtyp := typ.ElementType()
|
||||
subptrs := getPointerBitmap(targetData, subtyp, name)
|
||||
ptrs := big.NewInt(0)
|
||||
if subptrs.BitLen() == 0 {
|
||||
return ptrs
|
||||
}
|
||||
elementSize := targetData.TypeAllocSize(subtyp)
|
||||
for i := 0; i < typ.ArrayLength(); i++ {
|
||||
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
|
||||
ptrs.Or(ptrs, subptrs)
|
||||
}
|
||||
return ptrs
|
||||
default:
|
||||
panic("unknown type kind of global: " + name)
|
||||
}
|
||||
}
|
||||
|
||||
// markParentFunctions traverses all parent function calls (recursively) and
|
||||
// adds them to the set of marked functions. It only considers function calls:
|
||||
// any other uses of such a function is ignored.
|
||||
|
||||
@@ -7,13 +7,6 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
func TestAddGlobalsBitmap(t *testing.T) {
|
||||
t.Parallel()
|
||||
testTransform(t, "testdata/gc-globals", func(mod llvm.Module) {
|
||||
transform.AddGlobalsBitmap(mod)
|
||||
})
|
||||
}
|
||||
|
||||
func TestMakeGCStackSlots(t *testing.T) {
|
||||
t.Parallel()
|
||||
testTransform(t, "testdata/gc-stackslots", func(mod llvm.Module) {
|
||||
|
||||
+34
-3
@@ -11,6 +11,39 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
// OptimizePackage runs optimization passes over the LLVM module for the given
|
||||
// Go package.
|
||||
func OptimizePackage(mod llvm.Module, config *compileopts.Config) {
|
||||
optLevel, sizeLevel, _ := config.OptLevels()
|
||||
|
||||
// Run function passes for each function in the module.
|
||||
// These passes are intended to be run on each function right
|
||||
// after they're created to reduce IR size (and maybe also for
|
||||
// cache locality to improve performance), but for now they're
|
||||
// run here for each function in turn. Maybe this can be
|
||||
// improved in the future.
|
||||
builder := llvm.NewPassManagerBuilder()
|
||||
defer builder.Dispose()
|
||||
builder.SetOptLevel(optLevel)
|
||||
builder.SetSizeLevel(sizeLevel)
|
||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
||||
defer funcPasses.Dispose()
|
||||
builder.PopulateFunc(funcPasses)
|
||||
funcPasses.InitializeFunc()
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if fn.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
funcPasses.RunFunc(fn)
|
||||
}
|
||||
funcPasses.FinalizeFunc()
|
||||
|
||||
// Run TinyGo-specific optimization passes.
|
||||
if optLevel > 0 {
|
||||
OptimizeMaps(mod)
|
||||
}
|
||||
}
|
||||
|
||||
// Optimize runs a number of optimization and transformation passes over the
|
||||
// given module. Some passes are specific to TinyGo, others are generic LLVM
|
||||
// passes. You can set a preferred performance (0-3) and size (0-2) level and
|
||||
@@ -64,7 +97,6 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
|
||||
goPasses.Run(mod)
|
||||
|
||||
// Run TinyGo-specific optimization passes.
|
||||
OptimizeMaps(mod)
|
||||
OptimizeStringToBytes(mod)
|
||||
OptimizeReflectImplements(mod)
|
||||
OptimizeAllocs(mod, nil, nil)
|
||||
@@ -157,8 +189,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
|
||||
builder.Populate(modPasses)
|
||||
modPasses.Run(mod)
|
||||
|
||||
hasGCPass := AddGlobalsBitmap(mod)
|
||||
hasGCPass = MakeGCStackSlots(mod) || hasGCPass
|
||||
hasGCPass := MakeGCStackSlots(mod)
|
||||
if hasGCPass {
|
||||
if err := llvm.VerifyModule(mod, llvm.PrintMessageAction); err != nil {
|
||||
return []error{errors.New("GC pass caused a verification failure")}
|
||||
|
||||
Vendored
-33
@@ -1,33 +0,0 @@
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32-unknown-unknown-wasm"
|
||||
|
||||
%runtime._string = type { i8*, i32 }
|
||||
%runtime._interface = type { i32, i8* }
|
||||
|
||||
@globalInt = global i32 5
|
||||
@globalString = global %runtime._string zeroinitializer
|
||||
@globalInterface = global %runtime._interface zeroinitializer
|
||||
@constString = constant %runtime._string zeroinitializer
|
||||
@constInterface = constant %runtime._interface zeroinitializer
|
||||
@runtime.trackedGlobalsLength = external global i32
|
||||
@runtime.trackedGlobalsBitmap = external global [0 x i8]
|
||||
@runtime.trackedGlobalsStart = external global i32
|
||||
|
||||
define void @main() {
|
||||
%1 = load i32, i32* @globalInt
|
||||
%2 = load %runtime._string, %runtime._string* @globalString
|
||||
%3 = load %runtime._interface, %runtime._interface* @globalInterface
|
||||
%4 = load %runtime._string, %runtime._string* @constString
|
||||
%5 = load %runtime._interface, %runtime._interface* @constInterface
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @runtime.markGlobals() {
|
||||
; Very small subset of what runtime.markGlobals would really do.
|
||||
; Just enough to make sure the transformation is correct.
|
||||
%1 = load i32, i32* @runtime.trackedGlobalsStart
|
||||
%2 = load i32, i32* @runtime.trackedGlobalsLength
|
||||
%3 = getelementptr inbounds [0 x i8], [0 x i8]* @runtime.trackedGlobalsBitmap, i32 0, i32 0
|
||||
%4 = load i8, i8* %3
|
||||
ret void
|
||||
}
|
||||
Vendored
-31
@@ -1,31 +0,0 @@
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||
target triple = "wasm32-unknown-unknown-wasm"
|
||||
|
||||
%runtime._string = type { i8*, i32 }
|
||||
%runtime._interface = type { i32, i8* }
|
||||
|
||||
@globalInt = global i32 5
|
||||
@constString = constant %runtime._string zeroinitializer
|
||||
@constInterface = constant %runtime._interface zeroinitializer
|
||||
@runtime.trackedGlobalsLength = internal global i32 4
|
||||
@runtime.trackedGlobalsBitmap = external global [0 x i8]
|
||||
@runtime.trackedGlobalsStart = internal global i32 ptrtoint ({ %runtime._string, %runtime._interface }* @tinygo.trackedGlobals to i32)
|
||||
@tinygo.trackedGlobals = internal unnamed_addr global { %runtime._string, %runtime._interface } zeroinitializer
|
||||
@runtime.trackedGlobalsBitmap.1 = internal global [1 x i8] c"\09"
|
||||
|
||||
define void @main() {
|
||||
%1 = load i32, i32* @globalInt, align 4
|
||||
%2 = load %runtime._string, %runtime._string* getelementptr inbounds ({ %runtime._string, %runtime._interface }, { %runtime._string, %runtime._interface }* @tinygo.trackedGlobals, i32 0, i32 0), align 4
|
||||
%3 = load %runtime._interface, %runtime._interface* getelementptr inbounds ({ %runtime._string, %runtime._interface }, { %runtime._string, %runtime._interface }* @tinygo.trackedGlobals, i32 0, i32 1), align 4
|
||||
%4 = load %runtime._string, %runtime._string* @constString, align 4
|
||||
%5 = load %runtime._interface, %runtime._interface* @constInterface, align 4
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @runtime.markGlobals() {
|
||||
%1 = load i32, i32* @runtime.trackedGlobalsStart, align 4
|
||||
%2 = load i32, i32* @runtime.trackedGlobalsLength, align 4
|
||||
%3 = getelementptr inbounds [0 x i8], [0 x i8]* bitcast ([1 x i8]* @runtime.trackedGlobalsBitmap.1 to [0 x i8]*), i32 0, i32 0
|
||||
%4 = load i8, i8* %3, align 1
|
||||
ret void
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user