Compare commits

...

21 Commits

Author SHA1 Message Date
sago35 eafca3c583 WIP: support rp2040 2022-06-23 08:22:14 +09:00
sago35 a01c3423a1 WIP 2022-06-21 12:03:03 +09:00
Dan Kegel 8754f64f3b syscall.Getpagesize(): add test, implement for Linux and Windows 2022-06-12 01:15:42 +02:00
Ayke van Laethem caf405b01d reflect: add Value.UnsafePointer method
This was added in Go 1.18.
2022-06-12 01:08:02 +02:00
Ayke van Laethem bb65c5ce2b compiler: add support for type parameters (aka generics)
...that was surprisingly easy.
2022-06-11 20:41:16 +02:00
Ayke van Laethem 283fed16a5 builder: fix -no-debug linker flags
Show the correct error message when trying to strip debug information.

Also, remove the special case for GOOS=linux that was probably dead
code: it was only reachable on baremetal systems which were already
checked before.
2022-06-11 16:57:25 +02:00
sago35 76bba13963 usbhid: add support for mouse buttons (#2900)
* usbhid: add support for mouse buttons
2022-06-11 16:11:04 +02:00
Dan Kegel ada11090a2 smoketest: add regression test for 'tinygo test ./...', see #2892
tests/testing/recurse has two directories with tests;
"make smoketest" now does "tinygo test ./..." in that directory
and fails if it does not run both directories' tests.
2022-06-11 12:11:08 +02:00
José Carlos Chávez a07287d3c6 fix: fixes tinygo test ./... syntax. 2022-06-11 12:11:08 +02:00
sago35 1b2e764835 samd21,samd51,nrf52840: add support for USBHID (keyboard / mouse) 2022-06-11 09:44:09 +02:00
Nia Waldvogel 2c93a4085c transform (MakeGCStackSlots): do not move the stack chain pop earlier
Previously, the MakeGCStackSlots pass would attempt to pop the stack chain before a tail call.
This resulted in use-after-free bugs when the tail call allocated memory and used a value allocated by its caller.
Instead of trying to move the stack chain pop, remove the tail flag from the call.
2022-06-10 17:25:02 +02:00
sago35 906757603d wioterminal: fix I2C definition 2022-06-08 18:32:08 +02:00
Krzysztof Jagiello 5d16811199 Attach USB DP to the correct pin on Matrix Portal M4 2022-06-03 07:42:19 +02:00
Nia Waldvogel f2e576decf interp: do not unroll loops
This change triggers a revert whenever a basic block runs instructions at runtime twice.
As a result, a loop body with runtime-only instructions will no longer be unrolled.
This should help some extreme cases where loops can be expanded into hundreds or thousands of instructions.
2022-06-02 18:13:56 +02:00
Ayke van Laethem 2d61972475 gc: drop support for 'precise' globals
Precise globals require a whole program optimization pass that is hard
to support when building packages separately. This patch removes support
for these globals by converting the last use (Linux) to use
linker-defined symbols instead.

For details, see: https://github.com/tinygo-org/tinygo/issues/2870
2022-06-01 21:21:30 +02:00
Damian Gryski 5c488e3145 src/runtime: handle nil map write panics 2022-06-01 13:28:22 +02:00
Damian Gryski e45ff9c0e8 src/runtime: add per-map hash seeds 2022-06-01 13:28:22 +02:00
sago35 39805bca45 os, runtime: enable os.Stdin for baremetal target 2022-06-01 07:56:25 +02:00
Ayke van Laethem 97842b367c transform: run OptimizeMaps during package optimizations
This shrinks transform.Optimize() a little bit, working towards the goal
of https://github.com/tinygo-org/tinygo/issues/2870. I ran the smoke
tests and there is no practical downside: one test got smaller (??) and
one had a different .hex hash, but other than that there was no
difference.

This should also make TinyGo a liiitle bit faster but it's probably not
even measurable.
2022-05-30 20:39:42 +02:00
Ayke van Laethem 9246899b30 builder: move some code to transform package
The transform package is the more appropriate location for package-level
optimizations, to match `transform.Optimize` for whole-program
optimizations.
This is just a refactor, to make later changes easier to read.
2022-05-30 20:39:42 +02:00
Ayke van Laethem 04ace4de5f corpus: make non-working packages easy to uncomment
This makes it easier to test for changes, just remove the hash sign in
front of some packages to test them.
2022-05-30 14:21:35 +02:00
103 changed files with 5080 additions and 2952 deletions
+13
View File
@@ -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
View File
@@ -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)
}
}
+1
View File
@@ -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",
+9
View File
@@ -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
}
+18
View File
@@ -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
}
}
+1 -1
View File
@@ -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
View File
@@ -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()
+1 -1
View File
@@ -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
)
+13 -14
View File
@@ -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
View File
@@ -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
View File
@@ -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
+45
View File
@@ -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
}
+33
View File
@@ -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
View File
@@ -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 {
+18
View File
@@ -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
View File
@@ -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)
+40 -3
View File
@@ -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{}{}
+42
View File
@@ -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.
+31
View File
@@ -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())
}
}
}
+21
View File
@@ -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)
}
}
}
+37
View File
@@ -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)
}
}
}
+54
View File
@@ -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)
}
}
+11
View File
@@ -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
)
+1 -1
View File
@@ -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
+11
View File
@@ -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
)
+7 -7
View File
@@ -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
-884
View File
@@ -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() {
+628
View File
@@ -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
}
}
-716
View File
@@ -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() {
+460
View File
@@ -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
View File
@@ -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
+523
View File
@@ -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
}
+13 -1
View File
@@ -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
View File
@@ -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
}
+118
View File
@@ -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)
}
+164
View File
@@ -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
}
+225
View File
@@ -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()
}
+52
View File
@@ -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)
}
+63
View File
@@ -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)
}
+490
View File
@@ -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
}
+532
View File
@@ -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(),
}
+133
View File
@@ -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)
}
+52
View File
@@ -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)
}
+79
View File
@@ -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)
}
}
+105
View File
@@ -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
View File
@@ -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
}
+17
View File
@@ -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)
}
+5
View File
@@ -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() }
+3 -3
View File
@@ -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
View File
@@ -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"})
}
}
+6 -6
View File
@@ -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
-35
View File
@@ -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
View File
@@ -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)
}
+1 -1
View File
@@ -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
View File
@@ -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()
}
+11
View File
@@ -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"
+23
View File
@@ -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)
}
+5
View File
@@ -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")
+10
View File
@@ -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
+12
View File
@@ -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
+15 -3
View File
@@ -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)
+15 -3
View File
@@ -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)
+10
View File
@@ -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-- {
+10
View File
@@ -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")
}
+12
View File
@@ -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() {
+12
View File
@@ -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
+12
View File
@@ -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 {
+12
View File
@@ -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 {
+12
View File
@@ -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()
}
+16
View File
@@ -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)
+10
View File
@@ -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()
}
+15 -1
View File
@@ -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{})
}
+12
View File
@@ -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
+12
View File
@@ -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
+12
View File
@@ -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()
}
+12
View File
@@ -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)
+12
View File
@@ -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)
+12
View File
@@ -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)
+12
View File
@@ -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
+12
View File
@@ -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()
}
+10
View File
@@ -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)
}
-8
View File
@@ -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
+8
View File
@@ -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
}
+5
View File
@@ -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
+6
View File
@@ -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
}
+1 -1
View File
@@ -2,7 +2,7 @@
"inherits": [
"rp2040"
],
"serial": "uart",
"serial-port": ["acm:239a:80f1"],
"build-tags": ["feather_rp2040"],
"linkerscript": "targets/feather-rp2040.ld",
"extra-files": [
+1
View File
@@ -3,6 +3,7 @@
"rp2040"
],
"build-tags": ["pico"],
"serial-port": ["acm:2e8a:0003"],
"serial": "uart",
"linkerscript": "targets/pico.ld",
"extra-files": [
+41 -41
View File
@@ -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
+14
View File
@@ -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
}
+2
View File
@@ -0,0 +1,2 @@
add: 8
add: 8
@@ -0,0 +1,6 @@
package subdir
import "testing"
func TestSubdir(t *testing.T) {
}
+6
View File
@@ -0,0 +1,6 @@
package top
import "testing"
func TestTop(t *testing.T) {
}
+10 -164
View File
@@ -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
View File
@@ -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
View File
@@ -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")}
-33
View File
@@ -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
}
-31
View File
@@ -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