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
139 changed files with 4675 additions and 11010 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.
+68 -164
View File
@@ -8,7 +8,6 @@
package nxp
import (
"device/arm"
"runtime/volatile"
"unsafe"
)
@@ -379,6 +378,30 @@ func (clk Clock) setCcm(value uint32) {
}
}
func setSysPfd(value ...uint32) {
for i, val := range value {
pfd528 := CCM_ANALOG.PFD_528.Get() &
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
}
}
func setUsb1Pfd(value ...uint32) {
for i, val := range value {
pfd480 := CCM_ANALOG.PFD_480.Get() &
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
}
}
// PLL configuration for ARM
type ClockConfigArmPll struct {
LoopDivider uint32 // PLL loop divider. Valid range for divider value: 54-108. Fout=Fin*LoopDivider/2.
@@ -449,178 +472,59 @@ func (cfg ClockConfigSysPll) Configure(pfd ...uint32) {
setSysPfd(pfd...)
}
func setSysPfd(value ...uint32) {
for i, val := range value {
pfd528 := CCM_ANALOG.PFD_528.Get() &
^((CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_528_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_528_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_528_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_528.Set(pfd528 | (CCM_ANALOG_PFD_528_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_528.Set(pfd528 | (frac << (8 * uint32(i))))
}
}
// PHY configuration for USB HS
type ClockConfigUsbPhy struct {
Instance uint8 // USB PHY number (1 or 2)
XtalFreq uint32 // External reference clock frequency (Hz)
DCal uint32 // Decode to trim nominal 17.78mA current source
TxCal45DP uint32 // Decode to trim nominal 45-Ohm series Rp on USB D+
TxCal45DM uint32 // Decode to trim nominal 45-Ohm series Rp on USB D-
PllConfig ClockConfigUsbPll
}
// Configure initializes the USB HS (480 Mbit/s) PHY and PLL clocks, including
// the USB +3V regulator (PMU), for use as either USB host or device.
func (cfg ClockConfigUsbPhy) Configure() {
var (
usb *USB_Type
phy *USBPHY_Type
chrgDetectReg *volatile.Register32
chrgDetectMsk uint32
)
// Select appropriate peripherals based on receiver Instance
switch cfg.Instance {
case 1:
usb = USB1 // Select USB1 HS PHY/PLL
phy = USBPHY1 //
chrgDetectReg = &USB_ANALOG.USB1_CHRG_DETECT_SET
chrgDetectMsk = USB_ANALOG_USB1_CHRG_DETECT_SET_CHK_CHRG_B |
USB_ANALOG_USB1_CHRG_DETECT_SET_EN_B
case 2:
usb = USB2 // Select USB2 HS PHY/PLL
phy = USBPHY2 //
chrgDetectReg = &USB_ANALOG.USB2_CHRG_DETECT_SET
chrgDetectMsk = USB_ANALOG_USB2_CHRG_DETECT_SET_CHK_CHRG_B |
USB_ANALOG_USB2_CHRG_DETECT_SET_EN_B
default:
panic("nxp: invalid USB PHY")
}
// Configure and enable USB PLL clocks
cfg.PllConfig.Configure()
// Release PHY from reset
phy.CTRL.ClearBits(USBPHY_CTRL_SFTRST)
phy.CTRL.ClearBits(USBPHY_CTRL_CLKGATE)
// Enable power to USB PHY
phy.PWD.Set(0)
phy.CTRL.SetBits(USBPHY_CTRL_ENAUTOCLR_PHY_PWD | USBPHY_CTRL_ENAUTOCLR_CLKGATE |
// enable support for low-speed device connection, direct and indirect (hub)
USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3)
// Enable USB HS clocks gate
ClockIpUsbOh3.Enable(true)
// Reset USB peripheral
usb.USBCMD.SetBits(USB_USBCMD_RST)
// Add a delay after RST to ensure there is a USB D+ pullup sequence
nopDelay(400000)
// Enable USB LDO
PMU.REG_3P0.Set((PMU.REG_3P0.Get() & ^uint32(PMU_REG_3P0_OUTPUT_TRG_Msk)) |
(0x17 << PMU_REG_3P0_OUTPUT_TRG_Pos) | PMU_REG_3P0_ENABLE_LINREG)
// check whether we are connected to USB charger
chrgDetectReg.Set(chrgDetectMsk)
// Decode to trim nominal 17.78mA source for HS TX on USB D+/D-
phy.TX.Set((phy.TX.Get() &
^uint32(USBPHY_TX_D_CAL_Msk|USBPHY_TX_TXCAL45DN_Msk|USBPHY_TX_TXCAL45DP_Msk)) |
((cfg.DCal << USBPHY_TX_D_CAL_Pos) & USBPHY_TX_D_CAL_Msk) |
((cfg.TxCal45DM << USBPHY_TX_TXCAL45DN_Pos) & USBPHY_TX_TXCAL45DN_Msk) |
((cfg.TxCal45DP << USBPHY_TX_TXCAL45DP_Pos) & USBPHY_TX_TXCAL45DP_Msk))
}
// PLL configuration for USB
type ClockConfigUsbPll struct {
Instance uint8 // USB PLL number (1 or 2)
LoopDivider uint8 // PLL loop divider (0 [Fout=Fref*20] or 1 [Fout=Fref*22])
Src uint8 // PLL bypass clock source (0 [OSC24M] or 1 [CLK1_P & CLK1_N])
Pfd []uint32 // Phase fractional divisors (len=4, or nil for boot default)
Instance uint8 // USB PLL number (1 or 2)
LoopDivider uint8 // PLL loop divider: 0 - Fout=Fref*20, 1 - Fout=Fref*22
Src uint8 // Pll clock source, reference _clock_pll_clk_src
}
func (cfg ClockConfigUsbPll) Configure() {
// select USB peripheral registers based on receiver's Instance
switch cfg.Instance {
case 1: // USB1 PLL
if CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_ENABLE) {
// PLL already configured, enable USB clocks
CCM_ANALOG.PLL_USB1.SetBits(CCM_ANALOG_PLL_USB1_EN_USB_CLKS)
} else {
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) &
CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB1_BYPASS | src)
// reconfigure PLL
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) &
CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB1_ENABLE | CCM_ANALOG_PLL_USB1_POWER |
CCM_ANALOG_PLL_USB1_EN_USB_CLKS | sel)
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK) {
}
// disable bypass
CCM_ANALOG.PLL_USB1.ClearBits(CCM_ANALOG_PLL_USB1_BYPASS)
func (cfg ClockConfigUsbPll) Configure(pfd ...uint32) {
// update PFDs (if provided)
if nil != cfg.Pfd {
setUsb1Pfd(cfg.Pfd...)
}
switch cfg.Instance {
case 1:
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB1.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB1_BYPASS_Msk | src)
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB1_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)
CCM_ANALOG.PLL_USB1_SET.Set(
(CCM_ANALOG.PLL_USB1.Get() & ^uint32(CCM_ANALOG_PLL_USB1_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB1_ENABLE_Msk | CCM_ANALOG_PLL_USB1_POWER_Msk |
CCM_ANALOG_PLL_USB1_EN_USB_CLKS_Msk | sel)
for !CCM_ANALOG.PLL_USB1.HasBits(CCM_ANALOG_PLL_USB1_LOCK_Msk) {
}
case 2: // USB2 PLL
if CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_ENABLE) {
// PLL already configured, enable USB clocks
CCM_ANALOG.PLL_USB2.SetBits(CCM_ANALOG_PLL_USB2_EN_USB_CLKS)
} else {
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) &
CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB2_BYPASS | src)
// reconfigure PLL
sel := (uint32(cfg.LoopDivider) << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) &
CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB2_ENABLE | CCM_ANALOG_PLL_USB2_POWER |
CCM_ANALOG_PLL_USB2_EN_USB_CLKS | sel)
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK) {
}
// disable bypass
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS)
// disable bypass
CCM_ANALOG.PLL_USB1_CLR.Set(CCM_ANALOG_PLL_USB1_BYPASS_Msk)
// update PFDs after update
setUsb1Pfd(pfd...)
case 2:
// bypass PLL first
src := (uint32(cfg.Src) << CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Pos) & CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_BYPASS_CLK_SRC_Msk)) |
CCM_ANALOG_PLL_USB2_BYPASS_Msk | src)
sel := uint32((cfg.LoopDivider << CCM_ANALOG_PLL_USB2_DIV_SELECT_Pos) & CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)
CCM_ANALOG.PLL_USB2.Set(
(CCM_ANALOG.PLL_USB2.Get() & ^uint32(CCM_ANALOG_PLL_USB2_DIV_SELECT_Msk)) |
CCM_ANALOG_PLL_USB2_ENABLE_Msk | CCM_ANALOG_PLL_USB2_POWER_Msk |
CCM_ANALOG_PLL_USB2_EN_USB_CLKS_Msk | sel)
for !CCM_ANALOG.PLL_USB2.HasBits(CCM_ANALOG_PLL_USB2_LOCK_Msk) {
}
// disable bypass
CCM_ANALOG.PLL_USB2.ClearBits(CCM_ANALOG_PLL_USB2_BYPASS_Msk)
default:
panic("nxp: invalid USB PLL")
}
}
func setUsb1Pfd(value ...uint32) {
for i, val := range value {
pfd480 := CCM_ANALOG.PFD_480.Get() &
^((CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk | CCM_ANALOG_PFD_480_PFD0_FRAC_Msk) << (8 * uint32(i)))
frac := (val << CCM_ANALOG_PFD_480_PFD0_FRAC_Pos) & CCM_ANALOG_PFD_480_PFD0_FRAC_Msk
// disable the clock output first
CCM_ANALOG.PFD_480.Set(pfd480 | (CCM_ANALOG_PFD_480_PFD0_CLKGATE_Msk << (8 * uint32(i))))
// set the new value and enable output
CCM_ANALOG.PFD_480.Set(pfd480 | (frac << (8 * uint32(i))))
}
}
// We cannot use the sleep timer from this context (import cycle), but we need
// an approximate method to spin CPU cycles for short periods of time.
// go:inline
func nopDelay(cycles uint32) {
for i := uint32(0); i < cycles; i++ {
arm.Asm(`nop`)
}
}
-80
View File
@@ -252,83 +252,3 @@ func enableDcache(enable bool) {
}
}
}
// FlushDcache flushes data from cache to memory
//
// Normally FlushDcache is used when metadata written to memory will be used by
// a DMA or a bus-controller peripheral. Any data in the cache is written to
// memory. A copy remains in the cache, so this is typically used with special
// fields you will want to quickly access in the future. For data transmission,
// use FlushDeleteDcache.
//go:inline
func FlushDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCCMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
// DeleteDcache deletes data from the cache, without touching memory.
//
// Normally DeleteDcache is used before receiving data via DMA or from
// bus-controller peripherals which write to memory. You want to delete anything
// the cache may have stored, so your next read is certain to access the
// physical memory.
//go:inline
func DeleteDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCIMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
// FlushDeleteDcache flushes data from cache to memory, and delete it from the
// cache
//
// Normally FlushDeleteDcache is used when transmitting data via DMA or
// bus-controller peripherals which read from memory. You want any cached data
// written to memory, and then removed from the cache, because you no longer
// need to access the data after transmission.
//go:inline
func FlushDeleteDcache(addr, size uintptr) {
location := addr & 0xFFFFFFE0
endAddr := addr + size
arm.AsmFull(`
dsb 0xF
`, nil)
for {
SystemControl.DCCIMVAC.Set(uint32(location))
location += 32
if location >= endAddr {
break
}
}
arm.AsmFull(`
dsb 0xF
isb 0xF
`, nil)
}
+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)
}
}
View File
-258
View File
@@ -1,258 +0,0 @@
package main
import (
"machine"
"machine/usb"
"time"
)
var keyboard = machine.USB.Keyboard()
func main() {
for !machine.USB.Ready() {
}
println("USB HID keyboard demo")
for {
time.Sleep(5 * time.Second)
// Open a new text editor
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeySpace)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("kate"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(5 * time.Second)
// Use the io.Writer interface
keyboard.Write([]byte("TinyGo USB Keyboard Control Test\n"))
time.Sleep(2 * time.Second)
// Or manually specify keycodes and Unicode codepoints
testKeys([]Key{
// Print alphabet out-of-order
{Press: usb.KeyX},
{Press: usb.KeyY},
{Press: usb.KeyZ},
{Press: usb.KeyG},
{Press: usb.KeyH},
{Press: usb.KeyI},
{Press: usb.KeyJ},
{Press: usb.KeyK},
{Press: usb.KeyL},
{Press: usb.KeyM},
{Press: usb.KeyN},
{Press: usb.KeyO},
{Press: usb.KeyP},
{Press: usb.KeyQ},
{Press: usb.KeyR},
{Press: usb.KeyS},
{Press: usb.KeyT},
{Press: usb.KeyA},
{Press: usb.KeyB},
{Press: usb.KeyC},
{Press: usb.KeyD},
{Press: usb.KeyE},
{Press: usb.KeyF},
{Press: usb.KeyU},
{Press: usb.KeyV},
{Press: usb.KeyW},
// Pause 1 second
{Time: time.Second},
// Move cursor left x3
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
// Pause 1 second
{Time: time.Second},
// Highlight 6 symbols to the left
{Down: usb.KeyModifierShift},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Press: usb.KeyLeft},
{Up: usb.KeyModifierShift},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-X to cut
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Move to beginning of line
{Press: usb.KeyHome},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-V to paste
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Highlight 3 symbols to the right
{Down: usb.KeyModifierShift},
{Press: usb.KeyRight},
{Press: usb.KeyRight},
{Press: usb.KeyRight},
{Up: usb.KeyModifierShift},
// Use Ctrl-X to cut
{Down: usb.KeyModifierCtrl, Press: usb.KeyX, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Move to end of line
{Press: usb.KeyEnd},
// Pause 1 second
{Time: time.Second},
// Use Ctrl-V to paste
{Down: usb.KeyModifierCtrl, Press: usb.KeyV, Up: usb.KeyModifierCtrl},
// Pause 1 second
{Time: time.Second},
// Newline
{Press: usb.KeyEnter},
{Press: usb.KeyEnter},
}, 150*time.Millisecond)
// Highlight all text and delete
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyA)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(time.Second)
keyboard.Press(usb.KeyDelete)
time.Sleep(time.Second)
// Close window
keyboard.Down(usb.KeyModifierCtrl)
keyboard.Press(usb.KeyQ)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(2 * time.Second)
// Confirm discard file
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeyD)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(5 * time.Second)
// Open a new terminal
keyboard.Down(usb.KeyModifierAlt)
keyboard.Press(usb.KeySpace)
keyboard.Up(usb.KeyModifierAlt)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("konsole"))
keyboard.Press(usb.KeyEnter)
time.Sleep(5 * time.Second)
// Open serial connection (GNU screen)
keyboard.Write([]byte("screen /dev/ttyACM0 115200"))
time.Sleep(2 * time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(4 * time.Second)
// Write to UART
keyboard.Write([]byte("hello!"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
keyboard.Write([]byte("NO U"))
time.Sleep(time.Second)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
// Close serial connection (GNU screen)
keyboard.Down(usb.KeyModifierCtrl)
// Ctrl-X is the prefix sequence in my GNU screen configuration
keyboard.Press(usb.KeyX)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(time.Second)
// Backslash (Prefix-\) is the GNU screen command to kill window
keyboard.Press(usb.KeyBackslash)
time.Sleep(time.Second)
// Confirm "Kill window (Y/N)?" prompt
keyboard.Press(usb.KeyY)
keyboard.Press(usb.KeyEnter)
time.Sleep(2 * time.Second)
// Close terminal
keyboard.Down(usb.KeyModifierCtrl)
// Ctrl-D exits the shell (sends a resemblance of EOF, I believe?)
keyboard.Press(usb.KeyD)
keyboard.Up(usb.KeyModifierCtrl)
time.Sleep(25 * time.Second)
}
}
type Key struct {
Press usb.Keycode
Down usb.Keycode
Up usb.Keycode
Time time.Duration
}
func testKeys(key []Key, delay time.Duration) {
for _, k := range key {
if 0 != k.Down {
keyboard.Down(k.Down)
}
if 0 != k.Press {
keyboard.Press(k.Press)
}
if 0 != k.Up {
keyboard.Up(k.Up)
}
if 0 != k.Time {
time.Sleep(k.Time)
} else {
time.Sleep(delay)
}
}
}
func testInternationalLayout() {
// International keyboard layouts also supported
keyboard.Write([]byte("TinyGo USB Keyboard Layout Test\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Lowercase: abcdefghijklmnopqrstuvwxyz\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Uppercase: ABCDEFGHIJKLMNOPQRSTUVWXYZ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Numbers: 0123456789\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols1: !\"#$%&'()*+,-./\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols2: :;<=>?[\\]^_`{|}~\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols3: ¡¢£¤¥¦§¨©ª«¬­®¯°±\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Symbols4: ²³´µ¶·¸¹º»¼½¾¿×÷\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Grave: ÀÈÌÒÙàèìòù\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Acute: ÁÉÍÓÚÝáéíóúý\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Circumflex: ÂÊÎÔÛâêîôû\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Tilde: ÃÑÕãñõ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Diaeresis: ÄËÏÖÜäëïöüÿ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Cedilla: Çç\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Ring Above: Åå\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("AE: Ææ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Thorn: Þþ\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Sharp S: ß\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("O-Stroke: Øø\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Eth: Ðð\n"))
time.Sleep(250 * time.Millisecond)
keyboard.Write([]byte("Euro: €\n"))
}
-37
View File
@@ -1,37 +0,0 @@
// This is a echo console running on the device UART.
// Connect using default baudrate for this hardware, 8-N-1 with your terminal program.
package main
import (
"machine"
"time"
)
func main() {
uart := machine.Serial
uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n"))
input := make([]byte, 4096)
i := 0
for {
if uart.Buffered() > 0 {
data, _ := uart.ReadByte()
switch data {
case 13:
// return key
uart.Write([]byte("\r\n"))
uart.Write([]byte("You typed: "))
uart.Write(input[:i])
uart.Write([]byte("\r\n"))
i = 0
default:
// just echo the character
uart.WriteByte(data)
input[i] = data
i++
}
}
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
)
+15 -25
View File
@@ -5,7 +5,6 @@ package machine
import (
"device/nxp"
"machine/usb"
"runtime/interrupt"
)
@@ -57,21 +56,21 @@ const (
// Analog pins
const (
// = Pin // Dig [Pad] {ADC1/ADC2}
A0 = PA18 // D14 [AD_B1_02] { 7 / 7 }
A1 = PA19 // D15 [AD_B1_03] { 8 / 8 }
A2 = PA23 // D16 [AD_B1_07] { 12 / 12 }
A3 = PA22 // D17 [AD_B1_06] { 11 / 11 }
A4 = PA17 // D18 [AD_B1_01] { 6 / 6 }
A5 = PA16 // D19 [AD_B1_00] { 5 / 5 }
A6 = PA26 // D20 [AD_B1_10] { 15 / 15 }
A7 = PA27 // D21 [AD_B1_11] { 0 / 0 }
A8 = PA24 // D22 [AD_B1_08] { 13 / 13 }
A9 = PA25 // D23 [AD_B1_09] { 14 / 14 }
A10 = PA12 // D24 [AD_B0_12] { 1 / - }
A11 = PA13 // D25 [AD_B0_13] { 2 / - }
A12 = PA30 // D26 [AD_B1_14] { - / 3 }
A13 = PA31 // D27 [AD_B1_15] { - / 4 }
// = Pin // Dig | [Pad] {ADC1/ADC2}
A0 = PA18 // D14 | [AD_B1_02] { 7 / 7 }
A1 = PA19 // D15 | [AD_B1_03] { 8 / 8 }
A2 = PA23 // D16 | [AD_B1_07] { 12 / 12 }
A3 = PA22 // D17 | [AD_B1_06] { 11 / 11 }
A4 = PA17 // D18 | [AD_B1_01] { 6 / 6 }
A5 = PA16 // D19 | [AD_B1_00] { 5 / 5 }
A6 = PA26 // D20 | [AD_B1_10] { 15 / 15 }
A7 = PA27 // D21 | [AD_B1_11] { 0 / 0 }
A8 = PA24 // D22 | [AD_B1_08] { 13 / 13 }
A9 = PA25 // D23 | [AD_B1_09] { 14 / 14 }
A10 = PA12 // D24 | [AD_B0_12] { 1 / - }
A11 = PA13 // D25 | [AD_B0_13] { 2 / - }
A12 = PA30 // D26 | [AD_B1_14] { - / 3 }
A13 = PA31 // D27 | [AD_B1_15] { - / 4 }
)
// Default peripheral pins
@@ -106,15 +105,6 @@ func init() {
_UART7.Interrupt = interrupt.New(nxp.IRQ_LPUART7, _UART7.handleInterrupt)
}
// #=====================================================#
// | USB |
// #=====================================================#
var (
UART0 = usb.UART{Port: 0}
// HID0 = usb.HID{Port: 0}
// UART0 = &UART1
)
// #=====================================================#
// | UART |
// #===========#===========#=============#===============#
+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
}
}
-6
View File
@@ -1974,12 +1974,6 @@ func (tcc *TCC) Set(channel uint8, value uint32) {
}
}
func initUSB() {
// Configure USB D+/D- pins.
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
}
// 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
}
-3
View File
@@ -5,6 +5,3 @@ package machine
// Serial is a null device: writes to it are ignored.
var Serial = NullSerial{}
func InitSerial() {
}
-4
View File
@@ -5,7 +5,3 @@ package machine
// Serial is implemented via the default (usually the first) UART on the chip.
var Serial = DefaultUART
func InitSerial() {
Serial.Configure(UARTConfig{})
}
+13 -4
View File
@@ -3,8 +3,17 @@
package machine
// Serial is implemented via USB (USB-CDC).
var Serial = USB
func InitSerial() {
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 = NewUSBCDC()
-13
View File
@@ -1,13 +0,0 @@
//go:build usb.cdc
// +build usb.cdc
package machine
import "machine/usb"
var USB = &usb.CDC{}
func InitUSB() {
initUSB()
USB.Configure(usb.CDCConfig{})
}
-13
View File
@@ -1,13 +0,0 @@
//go:build usb.hid
// +build usb.hid
package machine
import "machine/usb"
var USB = &usb.HID{}
func InitUSB() {
initUSB()
USB.Configure(usb.HIDConfig{})
}
+183 -563
View File
@@ -1,5 +1,5 @@
//go:build nrf52840
// +build 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()
}
-277
View File
@@ -1,277 +0,0 @@
//go:build usb.cdc
// +build usb.cdc
package usb
import "unsafe"
//go:inline
func (d *dcd) endpointMaxPacketSize(endpoint uint8) uint32 {
switch endpointNumber(endpoint) {
case descCDCEndpointCtrl:
return descControlPacketSize
case descCDCEndpointStatus:
return descCDCStatusPacketSize
case descCDCEndpointDataRx:
return descCDCDataRxPacketSize
case descCDCEndpointDataTx:
return descCDCDataTxPacketSize
}
return descControlPacketSize
}
//go:inline
func (d *dcd) controlEndpoint() uint8 {
return descCDCEndpointCtrl
}
func (d *dcd) controlSetConfiguration() {
d.cdcConfigure()
}
func (d *dcd) controlClassRequest(sup dcdSetup) dcdStage {
// Switch on the recepient and direction of the request
switch sup.bmRequestType &
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
// --- INTERFACE Rx (OUT) ---
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
// Identify which request was received
switch sup.bRequest {
// CDC | SET LINE CODING (0x20):
case descCDCRequestSetLineCoding:
// line coding must contain exactly 7 bytes
if uint16(descCDCLineCodingSize) == sup.wLength {
d.controlReceive(
uintptr(unsafe.Pointer(&descCDC[d.cc.config-1].cx[0])),
uint32(descCDCLineCodingSize), true)
// CDC Line Coding packet receipt handling occurs in method
// controlComplete().
return dcdStageDataOut
}
// CDC | SET CONTROL LINE STATE (0x22):
case descCDCRequestSetControlLineState:
// Determine interface destination of the request
switch sup.wIndex {
// Control/status interface:
case descCDCInterfaceCtrl:
// CDC Control Line State packet receipt handling occurs in method
// controlComplete().
d.controlReceive(uintptr(0), 0, false)
return dcdStageStatusOut
default:
// Unhandled device interface
}
// CDC | SEND BREAK (0x23):
case descCDCRequestSendBreak:
d.controlReceive(uintptr(0), 0, false)
return dcdStageStatusOut
default:
// Unhandled request
}
default:
// Unhandled request recepient or direction
}
return dcdStageStall
}
func (d *dcd) controlGetInterfaceDescriptor(sup dcdSetup) bool {
switch sup.bRequest {
// GET DESCRIPTOR (0x06):
case descRequestStandardGetDescriptor:
d.controlGetDescriptor(sup)
return true
}
return false
}
func (d *dcd) controlGetDescriptor(sup dcdSetup) {
acm := &descCDC[d.cc.config-1]
dxn := uint8(0)
// Determine the type of descriptor being requested
switch sup.wValue >> 8 {
// Device descriptor
case descTypeDevice:
dxn = descLengthDevice
_ = copy(acm.dx[:], acm.device[:dxn])
// Configuration descriptor
case descTypeConfigure:
dxn = uint8(descCDCConfigSize)
_ = copy(acm.dx[:], acm.config[:dxn])
// String descriptor
case descTypeString:
if 0 == len(acm.locale) {
break // No string descriptors defined!
}
var sd []uint8
if 0 == uint8(sup.wValue) {
// setup.wIndex contains an arbitrary index referring to a collection of
// strings in some given language. This case (setup.wValue = [0x03]00)
// is a string request from the host to determine what that language is.
//
// In subsequent string requests, the host will populate setup.wIndex
// with the language code we return here in this string descriptor.
//
// This way all strings returned to the host are in the same language,
// whatever language that may be.
code := int(sup.wIndex)
if code >= len(acm.locale) {
code = 0
}
sd = acm.locale[code].descriptor[sup.wValue&0xFF][:]
} else {
// setup.wIndex now contains a language code, which we specified in a
// previous request (above: setup.wValue = [0x03]00). We need to locate
// the set of strings whose language matches the language code given in
// this new setup.wIndex.
for code := range acm.locale {
if sup.wIndex == acm.locale[code].language {
// Found language, check if string descriptor at given index exists
if int(sup.wValue&0xFF) < len(acm.locale[code].descriptor) {
// Found language with a string defined at the requested index.
//
// TODO: Add API methods to device controller that allows the user
// to provide these strings at/before driver initialization.
//
// For now, we just always use the descCommon* strings.
var s string
switch uint8(sup.wValue) {
case 1:
s = descCommonManufacturer
case 2:
s = descCommonProduct + " CDC-ACM"
case 3:
s = descCommonSerialNumber
}
// Construct a string descriptor dynamically to be transmitted on
// the serial bus.
sd = acm.locale[code].descriptor[int(sup.wValue&0xFF)][:]
// String descriptor format is 2-byte header + 2-bytes per rune
sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length
sd[1] = descTypeString // header[1] = descriptor type
// Copy UTF-8 string into string descriptor as UTF-16
for n, c := range s {
if 2+2*n >= len(sd) {
break
}
sd[2+2*n] = uint8(c)
sd[3+2*n] = 0
}
break // end search for matching language code
}
}
}
}
// Copy string descriptor into descriptor transmit buffer
if nil != sd && len(sd) >= 0 {
dxn = sd[0]
_ = copy(acm.dx[:], sd[:dxn])
}
// Device qualification descriptor
case descTypeQualification:
dxn = descLengthQualification
_ = copy(acm.dx[:], acm.qualif[:dxn])
// Alternate configuration descriptor
case descTypeOtherSpeedConfiguration:
// TODO
default:
// Unhandled descriptor type
}
if dxn > 0 {
if dxn > uint8(sup.wLength) {
dxn = uint8(sup.wLength)
}
flushCache(
uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn))
d.controlTransmit(
uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false)
}
}
// controlComplete handles the setup completion of control endpoint 0.
func (d *dcd) controlComplete() {
// First, switch on the type of request (standard, class, or vendor)
switch d.setup.bmRequestType & descRequestTypeTypeMsk {
// === CLASS REQUEST ===
case descRequestTypeTypeClass:
// Switch on the recepient and direction of the request
switch d.setup.bmRequestType &
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
// --- INTERFACE Rx (OUT) ---
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
// Identify which request was received
switch d.setup.bRequest {
// CDC | SET LINE CODING (0x20):
case descCDCRequestSetLineCoding:
acm := &descCDC[d.cc.config-1]
// Determine interface destination of the request
switch d.setup.wIndex {
// CDC-ACM Control Interface:
case descCDCInterfaceCtrl:
// Notify PHY to handle triggers like special baud rates, which
// signal to reboot into bootloader or begin receiving OTA updates
d.cdcSetLineCoding(acm.cx[:])
default:
// Unhandled device interface
}
// CDC | SET CONTROL LINE STATE (0x22):
case descCDCRequestSetControlLineState:
// Determine interface destination of the request
switch d.setup.wIndex {
// Control/status interface:
case descCDCInterfaceCtrl:
// DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02)
d.cdcSetLineState(d.setup.wValue)
default:
// Unhandled device interface
}
default:
// Unhandled request
}
default:
// Unhandled recepient or direction
}
default:
// Unhandled request type
}
}
-375
View File
@@ -1,375 +0,0 @@
//go:build usb.hid
// +build usb.hid
package usb
import "unsafe"
//go:inline
func (d *dcd) endpointMaxPacketSize(endpoint uint8) uint32 {
switch endpointNumber(endpoint) {
case descHIDEndpointCtrl:
return descControlPacketSize
case descHIDEndpointKeyboard:
return descHIDKeyboardTxPacketSize
case descHIDEndpointMouse:
return descHIDMouseTxPacketSize
case descHIDEndpointSerialRx: // == descHIDEndpointSerialTx
switch endpoint {
case rxEndpoint(endpoint):
return descHIDSerialRxPacketSize
case txEndpoint(endpoint):
return descHIDSerialTxPacketSize
}
case descHIDEndpointJoystick:
return descHIDJoystickTxPacketSize
case descHIDEndpointMediaKey:
return descHIDMediaKeyTxPacketSize
}
return descControlPacketSize
}
//go:inline
func (d *dcd) controlEndpoint() uint8 {
return descHIDEndpointCtrl
}
//go:inline
func (d *dcd) controlSetConfiguration() {
d.serialConfigure()
d.keyboardConfigure()
d.mouseConfigure()
d.joystickConfigure()
}
func (d *dcd) controlClassRequest(sup dcdSetup) dcdStage {
// Switch on the recepient and direction of the request
switch sup.bmRequestType &
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
// --- INTERFACE Rx (OUT) ---
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
// Identify which request was received
switch sup.bRequest {
// HID | SET REPORT (0x09)
case descHIDRequestSetReport:
if sup.wLength <= descHIDSxSize {
descHID[d.cc.config-1].cx[0] = 0xE9
d.controlReceive(
uintptr(unsafe.Pointer(&descHID[d.cc.config-1].cx[0])),
uint32(sup.wLength), true)
return dcdStageDataOut
}
// HID | SET IDLE (0x0A)
case descHIDRequestSetIdle:
idleRate := sup.wValue >> 8
// TBD: do we need to handle this request? wIndex contains the target
// interface of the request.
_ = idleRate
d.controlReceive(uintptr(0), 0, false)
return dcdStageStatusOut
default:
// Unhandled request
}
// --- INTERFACE Tx (IN) ---
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
// Identify which request was received
switch sup.bRequest {
// HID | GET REPORT (0x01)
case descHIDRequestGetReport:
reportType := uint8(sup.wValue >> 8)
reportID := uint8(sup.wValue)
// TBD: do we need to handle this request? wIndex contains the target
// interface of the request.
_, _ = reportType, reportID
d.controlTransmit(
d.controlStatusBuffer([]uint8{0, 0}),
2, false)
return dcdStageDataIn
default:
// Unhandled request
}
default:
// Unhandled request recepient or direction
}
return dcdStageStall
}
func (d *dcd) controlGetInterfaceDescriptor(sup dcdSetup) bool {
switch sup.bRequest {
// GET DESCRIPTOR (0x06):
case descRequestStandardGetDescriptor:
d.controlGetDescriptor(sup)
return true
// GET HID REPORT (0x01):
case descHIDRequestGetReport:
d.controlGetDescriptor(sup)
return true
}
return false
}
func (d *dcd) controlGetDescriptor(sup dcdSetup) {
hid := &descHID[d.cc.config-1]
dxn := uint8(0)
pos := uint8(0)
// Determine the type of descriptor being requested
switch sup.wValue >> 8 {
// Device descriptor
case descTypeDevice:
dxn = descLengthDevice
_ = copy(hid.dx[:], hid.device[:dxn])
// Configuration descriptor
case descTypeConfigure:
dxn = uint8(descHIDConfigSize)
_ = copy(hid.dx[:], hid.config[:dxn])
// String descriptor
case descTypeString:
if 0 == len(hid.locale) {
break // No string descriptors defined!
}
var sd []uint8
if 0 == uint8(sup.wValue) {
// setup.wIndex contains an arbitrary index referring to a collection of
// strings in some given language. This case (setup.wValue = [0x03]00)
// is a string request from the host to determine what that language is.
//
// In subsequent string requests, the host will populate setup.wIndex
// with the language code we return here in this string descriptor.
//
// This way all strings returned to the host are in the same language,
// whatever language that may be.
code := int(sup.wIndex)
if code >= len(hid.locale) {
code = 0
}
sd = hid.locale[code].descriptor[sup.wValue&0xFF][:]
} else {
// setup.wIndex now contains a language code, which we specified in a
// previous request (above: setup.wValue = [0x03]00). We need to locate
// the set of strings whose language matches the language code given in
// this new setup.wIndex.
for code := range hid.locale {
if sup.wIndex == hid.locale[code].language {
// Found language, check if string descriptor at given index exists
if int(sup.wValue&0xFF) < len(hid.locale[code].descriptor) {
// Found language with a string defined at the requested index.
//
// TODO: Add API methods to device controller that allows the user
// to provide these strings at/before driver initialization.
//
// For now, we just always use the descCommon* strings.
var s string
switch uint8(sup.wValue) {
case 1:
s = descCommonManufacturer
case 2:
s = descCommonProduct + " HID"
case 3:
s = descCommonSerialNumber
}
// Construct a string descriptor dynamically to be transmitted on
// the serial bus.
sd = hid.locale[code].descriptor[int(sup.wValue&0xFF)][:]
// String descriptor format is 2-byte header + 2-bytes per rune
sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length
sd[1] = descTypeString // header[1] = descriptor type
// Copy UTF-8 string into string descriptor as UTF-16
for n, c := range s {
if 2+2*n >= len(sd) {
break
}
sd[2+2*n] = uint8(c)
sd[3+2*n] = 0
}
break // end search for matching language code
}
}
}
}
// Copy string descriptor into descriptor transmit buffer
if nil != sd && len(sd) >= 0 {
dxn = sd[0]
_ = copy(hid.dx[:], sd[:dxn])
}
// Device qualification descriptor
case descTypeQualification:
dxn = descLengthQualification
_ = copy(hid.dx[:], hid.qualif[:dxn])
// Alternate configuration descriptor
case descTypeOtherSpeedConfiguration:
// TODO
// HID descriptor
case descTypeHID:
// Determine interface destination of the request
switch sup.wIndex {
case descHIDInterfaceKeyboard:
pos = descHIDConfigKeyboardPos
case descHIDInterfaceMouse:
pos = descHIDConfigMousePos
case descHIDInterfaceSerial:
pos = descHIDConfigSerialPos
case descHIDInterfaceJoystick:
pos = descHIDConfigJoystickPos
case descHIDInterfaceMediaKey:
pos = descHIDConfigMediaKeyPos
default:
// Unhandled HID interface
}
if 0 != pos {
dxn = descLengthInterface
_ = copy(hid.dx[:], hid.config[pos:pos+dxn])
}
// HID report descriptor
case descTypeHIDReport:
// Determine interface destination of the request
switch sup.wIndex {
case descHIDInterfaceKeyboard:
dxn = uint8(len(descHIDReportKeyboard))
_ = copy(hid.dx[:], descHIDReportKeyboard[:])
case descHIDInterfaceMouse:
dxn = uint8(len(descHIDReportMouse))
_ = copy(hid.dx[:], descHIDReportMouse[:])
case descHIDInterfaceSerial:
dxn = uint8(len(descHIDReportSerial))
_ = copy(hid.dx[:], descHIDReportSerial[:])
case descHIDInterfaceJoystick:
dxn = uint8(len(descHIDReportJoystick))
_ = copy(hid.dx[:], descHIDReportJoystick[:])
case descHIDInterfaceMediaKey:
dxn = uint8(len(descHIDReportMediaKey))
_ = copy(hid.dx[:], descHIDReportMediaKey[:])
default:
// Unhandled HID interface
}
default:
// Unhandled descriptor type
}
if dxn > 0 {
if dxn > uint8(sup.wLength) {
dxn = uint8(sup.wLength)
}
flushCache(
uintptr(unsafe.Pointer(&hid.dx[0])), uintptr(dxn))
d.controlTransmit(
uintptr(unsafe.Pointer(&hid.dx[0])), uint32(dxn), false)
}
}
// controlComplete handles the setup completion of control endpoint 0.
func (d *dcd) controlComplete() {
// First, switch on the type of request (standard, class, or vendor)
switch d.setup.bmRequestType & descRequestTypeTypeMsk {
// === CLASS REQUEST ===
case descRequestTypeTypeClass:
// Switch on the recepient and direction of the request
switch d.setup.bmRequestType &
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
// --- INTERFACE Rx (OUT) ---
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
// Identify which request was received
switch d.setup.bRequest {
// HID | SET REPORT (0x09)
case descHIDRequestSetReport:
hid := &descHID[d.cc.config-1]
// Determine interface destination of the request
switch d.setup.wIndex {
// HID Keyboard Interface
case descHIDInterfaceKeyboard:
// Determine the type of descriptor being requested
switch d.setup.wValue >> 8 {
// Configuration descriptor
case descTypeConfigure:
if 1 == d.setup.wLength {
hid.keyboard.led = hid.cx[0]
d.controlTransmit(uintptr(0), 0, false)
}
default:
// Unhandled descriptor type
}
// HID Serial Interface
case descHIDInterfaceSerial:
// Determine the type of descriptor being requested
switch d.setup.wValue >> 8 {
// String descriptor
case descTypeString:
if d.setup.wLength >= 4 && 0x68C245A9 == packU32(hid.cx[0:4]) {
d.enableSOF(true, descHIDInterfaceCount)
}
default:
// Unhandled descriptor type
}
default:
// Unhandled device interface
}
default:
// Unhandled request
}
default:
// Unhandled recepient or direction
}
default:
// Unhandled request type
}
}
-428
View File
@@ -1,428 +0,0 @@
package usb
// Implementation of target-agnostic USB device controller driver (dcd).
//
// The types, constants, and methods defined in this unit are applicable to all
// targets. It was designed to complement the device hardware abstraction (dhw)
// implemented for each target, providing common/shared functionality and
// defining a standard interface with which the dhw must adhere.
import (
"runtime/volatile"
"unsafe"
)
// dcdCount defines the number of USB cores to configure for device mode. It is
// computed as the sum of all declared device configuration descriptors.
const dcdCount = descCDCCount + descHIDCount
// dcdInstance provides statically-allocated instances of each USB device
// controller configured on this platform.
var dcdInstance [dcdCount]dcd
// dhwInstance provides statically-allocated instances of each USB hardware
// abstraction for ports configured as device on this platform.
var dhwInstance [dcdCount]dhw
// dcd implements a generic USB device controller driver (dcd) for all targets.
type dcd struct {
*dhw // USB hardware abstraction layer
core *core // Parent USB core this instance is attached to
port int // USB port index
cc class // USB device class
id int // USB device controller index
st volatile.Register8 // USB device state
}
// initDCD initializes and assigns a free device controller instance to the
// given USB port. Returns the initialized device controller or nil if no free
// device controller instances remain.
func initDCD(port int, speed Speed, class class) (*dcd, status) {
if 0 == dcdCount {
return nil, statusInvalid // Must have defined device controllers
}
switch class.id {
case classDeviceCDC:
if 0 == class.config || class.config > descCDCCount {
return nil, statusInvalid // Must have defined descriptors
}
default:
}
// Return the first instance whose assigned core is currently nil.
for i := range dcdInstance {
if nil == dcdInstance[i].core {
// Initialize device controller.
dcdInstance[i].dhw = allocDHW(port, i, speed, &dcdInstance[i])
dcdInstance[i].core = &coreInstance[port]
dcdInstance[i].port = port
dcdInstance[i].cc = class
dcdInstance[i].id = i
dcdInstance[i].setState(dcdStateNotReady)
return &dcdInstance[i], statusOK
}
}
return nil, statusBusy // No free device controller instances available.
}
// class returns the receiver's current device class configuration.
func (d *dcd) class() class { return d.cc }
// dcdSetupSize defines the size (bytes) of a USB standard setup packet.
const dcdSetupSize = unsafe.Sizeof(dcdSetup{}) // 8 bytes
// dcdSetup contains the USB standard setup packet used to configure a device.
type dcdSetup struct {
bmRequestType uint8
bRequest uint8
wValue uint16
wIndex uint16
wLength uint16
}
// setupFrom decodes and returns a USB standard setup packet located at the
// memory address pointed to by addr.
func setupFrom(addr uintptr) (s dcdSetup) {
var u uint64
for i := uintptr(0); i < dcdSetupSize; i++ {
u |= uint64(*(*uint8)(unsafe.Pointer(addr + i))) << (i << 3)
}
s.set(u)
return
}
// setup decodes and returns a USB standard setup packet stored in the given
// byte slice b.
func setup(b []uint8) dcdSetup {
if len(b) >= int(dcdSetupSize) {
return dcdSetup{
bmRequestType: b[0],
bRequest: b[1],
wValue: packU16(b[2:]),
wIndex: packU16(b[4:]),
wLength: packU16(b[6:]),
}
}
return dcdSetup{}
}
//go:inline
func (s *dcdSetup) set(u uint64) {
s.bmRequestType = uint8(u & 0xFF)
s.bRequest = uint8((u & 0xFF00) >> 8)
s.wValue = uint16((u & 0xFFFF0000) >> 16)
s.wIndex = uint16((u & 0xFFFF00000000) >> 32)
s.wLength = uint16((u & 0xFFFF000000000000) >> 48)
}
// pack returns the receiver USB standard setup packet s encoded as uint64.
//go:inline
func (s dcdSetup) pack() uint64 {
return ((uint64(s.bmRequestType) & 0xFF) << 0) |
((uint64(s.bRequest) & 0xFF) << 8) |
((uint64(s.wValue) & 0xFFFF) << 16) |
((uint64(s.wIndex) & 0xFFFF) << 32) |
((uint64(s.wLength) & 0xFFFF) << 48)
}
// direction parses the direction bit from the bmRequestType field of a SETUP
// packet, returning 0 for OUT (Rx) and 1 for IN (Tx) requests.
//go:inline
func (s dcdSetup) direction() uint8 {
return (s.bmRequestType & descRequestTypeDirMsk) >> descRequestTypeDirPos
}
//go:inline
func (s dcdSetup) equals(t dcdSetup) bool {
return s.bmRequestType == t.bmRequestType && s.bRequest == t.bRequest &&
s.wValue == t.wValue && s.wIndex == t.wIndex && s.wLength == t.wLength
}
// dcdState defines the current state of the device class driver.
type dcdState uint8
const (
dcdStateNotReady dcdState = iota // initial state, before END_OF_RESET
dcdStateDefault // after END_OF_RESET, before SET_ADDRESS
dcdStateAddressed // after SET_ADDRESS, before SET_CONFIGURATION
dcdStateConfigured // after SET_CONFIGURATION, operational state
dcdStateSuspended // while operational, after SUSPEND
)
func (d *dcd) state() dcdState { return dcdState(d.st.Get()) }
func (d *dcd) setState(state dcdState) (ok bool) {
curr := d.state()
switch state {
case dcdStateNotReady:
ok = true
case dcdStateDefault:
ok = curr == dcdStateNotReady || curr == dcdStateDefault
case dcdStateAddressed:
ok = curr == dcdStateDefault
case dcdStateConfigured:
ok = curr == dcdStateAddressed || curr == dcdStateConfigured || curr == dcdStateSuspended
case dcdStateSuspended:
ok = curr == dcdStateAddressed || curr == dcdStateConfigured || curr == dcdStateSuspended
default:
ok = false
}
if ok {
d.st.Set(uint8(state))
}
return
}
// dcdEvent is used to describe virtual interrupts on the USB bus to a device
// controller.
//
// Since the device controller software is intended for use with multiple TinyGo
// targets, all of which may not have exactly the same USB bus interrupts, a
// "virtual interrupt" is defined that is common to all targets. The target's
// hardware implementation (type dhw) is responsible for translating real system
// interrupts it receives into the appropriate virtual interrupt code, defined
// below, and notifying the device controller via method (*dcd).event(dcdEvent).
type dcdEvent struct {
id uint8
setup dcdSetup
mask uint32
}
// Enumerated constants for all possible USB device controller interrupt codes.
const (
dcdEventInvalid uint8 = iota // Invalid interrupt
dcdEventStatusReset // USB RESET received
dcdEventStatusResume // USB RESUME condition
dcdEventStatusSuspend // USB SUSPEND received
dcdEventStatusError // USB error condition detected on bus
dcdEventDeviceReady // USB PHY powered and ready to _go_
dcdEventDeviceAddress // USB device SET_ADDRESS complete
dcdEventDeviceConfiguration // USB device SET_CONFIGURATION complete
dcdEventControlSetup // USB SETUP received
dcdEventControlComplete // USB control request complete
dcdEventTransferComplete // USB data transfer complete
dcdEventTimer // USB (system) timer
)
func (d *dcd) event(ev dcdEvent) {
switch ev.id {
case dcdEventStatusReset:
d.setState(dcdStateNotReady)
case dcdEventStatusResume:
d.setState(dcdStateConfigured)
case dcdEventStatusSuspend:
d.setState(dcdStateSuspended)
case dcdEventDeviceReady:
if d.setState(dcdStateDefault) {
// Configure and enable control endpoint 0
d.endpointEnable(0, true, 0)
}
case dcdEventDeviceAddress:
// -- ** IMPORTANT ** --
// dcdEventDeviceAddress must be triggered by the target driver, because
// different MCUs require setting the device address at different times
// during the enumeration process.
d.setState(dcdStateAddressed)
case dcdEventDeviceConfiguration:
d.setState(dcdStateConfigured)
case dcdEventControlSetup:
// On control endpoint 0 setup events, the ev.setup field will be defined.
// We overwrite the receiver's setup field, leaving it unmodified throughout
// all transactions of a control transfer. It is only cleared once the
// completion event dcdEventControlComplete has been called and finished
// processing, or if its initial processing fails due to error.
d.setup = ev.setup
d.stage = d.controlSetup(ev.setup)
switch d.stage {
case dcdStageDataIn, dcdStageDataOut:
// TBD: control endpoint data transfer
case dcdStageStatusIn, dcdStageStatusOut:
// TBD: control endpoint status transfer
case dcdStageStall:
d.controlStall(true, ev.setup.direction())
case dcdStageSetup:
fallthrough
default:
// TBD: no stage transition occurred
}
case dcdEventControlComplete:
d.controlComplete()
// clear the active SETUP packet once the control transfer completes.
d.setup = dcdSetup{}
case dcdEventTransferComplete:
// TBD: data endpoint transfer complete
case dcdEventInvalid, dcdEventStatusError, dcdEventTimer:
fallthrough
default:
// TBD: unhandled events
}
}
// dcdStage represents the stage of a USB control transfer.
type dcdStage uint8
// Enumerated constants for all possible USB control transfer stages.
const (
dcdStageSetup dcdStage = iota // Indicates no stage transition required
dcdStageDataIn // IN data transfer
dcdStageDataOut // OUT data transfer
dcdStageStatusIn // IN status request
dcdStageStatusOut // OUT status request
dcdStageStall // Unhandled or invalid request
)
// controlSetup handles setup messages on control endpoint 0.
func (d *dcd) controlSetup(sup dcdSetup) dcdStage {
// First, switch on the type of request (standard, class, or vendor)
switch sup.bmRequestType & descRequestTypeTypeMsk {
// === STANDARD REQUEST ===
case descRequestTypeTypeStandard:
// Switch on the recepient and direction of the request
switch sup.bmRequestType &
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
// --- DEVICE Rx (OUT) ---
case descRequestTypeRecipientDevice | descRequestTypeDirOut:
// Identify which request was received
switch sup.bRequest {
// SET ADDRESS (0x05):
case descRequestStandardSetAddress:
d.setDeviceAddress(sup.wValue)
d.controlReceive(uintptr(0), 0, false)
return dcdStageStatusOut
// SET CONFIGURATION (0x09):
case descRequestStandardSetConfiguration:
d.cc.config = int(sup.wValue)
if 0 == d.cc.config || d.cc.config > dcdCount {
// Use default if invalid index received
d.cc.config = 1
}
d.event(dcdEvent{id: dcdEventDeviceConfiguration})
d.controlSetConfiguration()
d.controlReceive(uintptr(0), 0, false)
return dcdStageStatusOut
default:
// Unhandled request
}
// --- DEVICE Tx (IN) ---
case descRequestTypeRecipientDevice | descRequestTypeDirIn:
// Identify which request was received
switch sup.bRequest {
// GET STATUS (0x00):
case descRequestStandardGetStatus:
d.controlTransmit(
d.controlStatusBuffer([]uint8{0, 0}),
2, false)
return dcdStageDataIn
// GET DESCRIPTOR (0x06):
case descRequestStandardGetDescriptor:
d.controlGetDescriptor(sup)
return dcdStageDataIn
// GET CONFIGURATION (0x08):
case descRequestStandardGetConfiguration:
d.controlTransmit(
d.controlStatusBuffer([]uint8{
uint8(d.cc.config),
}),
1, false)
return dcdStageDataIn
default:
// Unhandled request
}
// --- INTERFACE Tx (IN) ---
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
if d.controlGetInterfaceDescriptor(sup) {
return dcdStageDataIn
}
// --- ENDPOINT Rx (OUT) ---
case descRequestTypeRecipientEndpoint | descRequestTypeDirOut:
// Identify which request was received
switch sup.bRequest {
// CLEAR FEATURE (0x01):
case descRequestStandardClearFeature:
d.endpointClearFeature(uint8(sup.wIndex))
d.controlReceive(uintptr(0), 0, false)
return dcdStageStatusOut
// SET FEATURE (0x03):
case descRequestStandardSetFeature:
d.endpointSetFeature(uint8(sup.wIndex))
d.controlReceive(uintptr(0), 0, false)
return dcdStageStatusOut
default:
// Unhandled request
}
// --- ENDPOINT Tx (IN) ---
case descRequestTypeRecipientEndpoint | descRequestTypeDirIn:
// Identify which request was received
switch sup.bRequest {
// GET STATUS (0x00):
case descRequestStandardGetStatus:
status := d.endpointStatus(uint8(sup.wIndex))
d.controlTransmit(
d.controlStatusBuffer([]uint8{
uint8(status),
uint8(status >> 8),
}),
2, false)
return dcdStageDataIn
default:
// Unhandled request
}
default:
// Unhandled request recepient or direction
}
// === CLASS REQUEST ===
case descRequestTypeTypeClass:
// Forward all class requests to the device class implementation.
return d.controlClassRequest(sup)
case descRequestTypeTypeVendor:
default:
// Unhandled request type
}
// All successful requests return early. If we reach this point, the request
// was invalid or unhandled. Stall the endpoint.
return dcdStageStall
}
-414
View File
@@ -1,414 +0,0 @@
//go:build usb.cdc
// +build usb.cdc
package usb
// descHIDCount defines the number of USB cores that may be configured as a
// composite (keyboard + mouse + joystick) human interface device (HID).
const descHIDCount = 0
// USB CDC constants defined per specification.
const (
// Device class
descCDCTypeComm = 0x02 // communication/control
descCDCTypeData = 0x0A // data
// Communication/control subclass
descCDCSubNone = 0x00
descCDCSubDirectLineControl = 0x01
descCDCSubAbstractControl = 0x02
descCDCSubTelephoneControl = 0x03
descCDCSubMultiChannelControl = 0x04
descCDCSubCAPIControl = 0x05
descCDCSubEthernetNetworkingControl = 0x06
descCDCSubATMNetworkingControl = 0x07
descCDCSubWirelessHandsetControl = 0x08
descCDCSubDeviceManagement = 0x09
descCDCSubMobileDirectLine = 0x0A
descCDCSubOBEX = 0x0B
descCDCSubEthernetEmulation = 0x0C
// Communication/control protocol
descCDCProtoNone = 0x00 // also for data class
descCDCProtoAT250 = 0x01
descCDCProtoATPCCA101 = 0x02
descCDCProtoATPCCA101AnnexO = 0x03
descCDCProtoATGSM707 = 0x04
descCDCProtoAT3GPP27007 = 0x05
descCDCProtoATTIACDMA = 0x06
descCDCProtoEthernetEmulation = 0x07
descCDCProtoExternal = 0xFE
descCDCProtoVendorSpecific = 0xFF // also for data class
// Data protocol
descCDCProtoPyhsicalInterface = 0x30
descCDCProtoHDLC = 0x31
descCDCProtoTransparent = 0x32
descCDCProtoManagement = 0x50
descCDCProtoDataLinkQ931 = 0x51
descCDCProtoDataLinkQ921 = 0x52
descCDCProtoDataCompressionV42BIS = 0x90
descCDCProtoEuroISDN = 0x91
descCDCProtoRateAdaptionISDNV24 = 0x92
descCDCProtoCAPICommands = 0x93
descCDCProtoHostBasedDriver = 0xFD
descCDCProtoUnitFunctional = 0xFE
// Functional descriptor length
descCDCFuncLengthHeader = 5
descCDCFuncLengthCallManagement = 5
descCDCFuncLengthAbstractControl = 4
descCDCFuncLengthUnion = 5
// Functional descriptor type
descCDCFuncTypeHeader = 0x00
descCDCFuncTypeCallManagement = 0x01
descCDCFuncTypeAbstractControl = 0x02
descCDCFuncTypeDirectLine = 0x03
descCDCFuncTypeTelephoneRinger = 0x04
descCDCFuncTypeTelephoneReport = 0x05
descCDCFuncTypeUnion = 0x06
descCDCFuncTypeCountrySelect = 0x07
descCDCFuncTypeTelephoneModes = 0x08
descCDCFuncTypeTerminal = 0x09
descCDCFuncTypeNetworkChannel = 0x0A
descCDCFuncTypeProtocolUnit = 0x0B
descCDCFuncTypeExtensionUnit = 0x0C
descCDCFuncTypeMultiChannel = 0x0D
descCDCFuncTypeCAPIControl = 0x0E
descCDCFuncTypeEthernetNetworking = 0x0F
descCDCFuncTypeATMNetworking = 0x10
descCDCFuncTypeWirelessControl = 0x11
descCDCFuncTypeMobileDirectLine = 0x12
descCDCFuncTypeMDLMDetail = 0x13
descCDCFuncTypeDeviceManagement = 0x14
descCDCFuncTypeOBEX = 0x15
descCDCFuncTypeCommandSet = 0x16
descCDCFuncTypeCommandSetDetail = 0x17
descCDCFuncTypeTelephoneControl = 0x18
descCDCFuncTypeOBEXServiceID = 0x19
// Standard request
descCDCRequestSendEncapsulatedCommand = 0x00 // CDC request SEND_ENCAPSULATED_COMMAND
descCDCRequestGetEncapsulatedResponse = 0x01 // CDC request GET_ENCAPSULATED_RESPONSE
descCDCRequestSetCommFeature = 0x02 // CDC request SET_COMM_FEATURE
descCDCRequestGetCommFeature = 0x03 // CDC request GET_COMM_FEATURE
descCDCRequestClearCommFeature = 0x04 // CDC request CLEAR_COMM_FEATURE
descCDCRequestSetAuxLineState = 0x10 // CDC request SET_AUX_LINE_STATE
descCDCRequestSetHookState = 0x11 // CDC request SET_HOOK_STATE
descCDCRequestPulseSetup = 0x12 // CDC request PULSE_SETUP
descCDCRequestSendPulse = 0x13 // CDC request SEND_PULSE
descCDCRequestSetPulseTime = 0x14 // CDC request SET_PULSE_TIME
descCDCRequestRingAuxJack = 0x15 // CDC request RING_AUX_JACK
descCDCRequestSetLineCoding = 0x20 // CDC request SET_LINE_CODING
descCDCRequestGetLineCoding = 0x21 // CDC request GET_LINE_CODING
descCDCRequestSetControlLineState = 0x22 // CDC request SET_CONTROL_LINE_STATE
descCDCRequestSendBreak = 0x23 // CDC request SEND_BREAK
descCDCRequestSetRingerParams = 0x30 // CDC request SET_RINGER_PARAMS
descCDCRequestGetRingerParams = 0x31 // CDC request GET_RINGER_PARAMS
descCDCRequestSetOperationParam = 0x32 // CDC request SET_OPERATION_PARAM
descCDCRequestGetOperationParam = 0x33 // CDC request GET_OPERATION_PARAM
descCDCRequestSetLineParams = 0x34 // CDC request SET_LINE_PARAMS
descCDCRequestGetLineParams = 0x35 // CDC request GET_LINE_PARAMS
descCDCRequestDialDigits = 0x36 // CDC request DIAL_DIGITS
descCDCRequestSetUnitParameter = 0x37 // CDC request SET_UNIT_PARAMETER
descCDCRequestGetUnitParameter = 0x38 // CDC request GET_UNIT_PARAMETER
descCDCRequestClearUnitParameter = 0x39 // CDC request CLEAR_UNIT_PARAMETER
descCDCRequestSetEthernetMulticastFilters = 0x40 // CDC request SET_ETHERNET_MULTICAST_FILTERS
descCDCRequestSetEthernetPowPatternFilter = 0x41 // CDC request SET_ETHERNET_POW_PATTER_FILTER
descCDCRequestGetEthernetPowPatternFilter = 0x42 // CDC request GET_ETHERNET_POW_PATTER_FILTER
descCDCRequestSetEthernetPacketFilter = 0x43 // CDC request SET_ETHERNET_PACKET_FILTER
descCDCRequestGetEthernetStatistic = 0x44 // CDC request GET_ETHERNET_STATISTIC
descCDCRequestSetATMDataFormat = 0x50 // CDC request SET_ATM_DATA_FORMAT
descCDCRequestGetATMDeviceStatistics = 0x51 // CDC request GET_ATM_DEVICE_STATISTICS
descCDCRequestSetATMDefaultVC = 0x52 // CDC request SET_ATM_DEFAULT_VC
descCDCRequestGetATMVCStatistics = 0x53 // CDC request GET_ATM_VC_STATISTICS
descCDCRequestMDLMSpecificRequestsMask = 0x7F // CDC request MDLM_SPECIFIC_REQUESTS_MASK
// Notification type
descCDCNotifyNetworkConnection = 0x00 // CDC notify NETWORK_CONNECTION
descCDCNotifyResponseAvail = 0x01 // CDC notify RESPONSE_AVAIL
descCDCNotifyAuxJackHookState = 0x08 // CDC notify AUX_JACK_HOOK_STATE
descCDCNotifyRingDetect = 0x09 // CDC notify RING_DETECT
descCDCNotifySerialState = 0x20 // CDC notify SERIAL_STATE
descCDCNotifyCallStateChange = 0x28 // CDC notify CALL_STATE_CHANGE
descCDCNotifyLineStateChange = 0x29 // CDC notify LINE_STATE_CHANGE
descCDCNotifyConnectionSpeedChange = 0x2A // CDC notify CONNECTION_SPEED_CHANGE
// Feature select
descCDCFeatureAbstractState = 0x01 // CDC feature select ABSTRACT_STATE
descCDCFeatureCountrySetting = 0x02 // CDC feature select COUNTRY_SETTING
// Control signal
descCDCControlSigBitmapCarrierActivation = 0x02 // CDC control signal CARRIER_ACTIVATION
descCDCControlSigBitmapDTEPresence = 0x01 // CDC control signal DTE_PRESENCE
// UART emulated state
descCDCUARTStateRxCarrier = 0x01 // UART state RX_CARRIER
descCDCUARTStateTxCarrier = 0x02 // UART state TX_CARRIER
descCDCUARTStateBreak = 0x04 // UART state BREAK
descCDCUARTStateRingSignal = 0x08 // UART state RING_SIGNAL
descCDCUARTStateFraming = 0x10 // UART state FRAMING
descCDCUARTStateParity = 0x20 // UART state PARITY
descCDCUARTStateOverrun = 0x40 // UART state OVERRUN
)
const (
// Size of all CDC-ACM configuration descriptors.
descCDCConfigSize = uint16(
descLengthConfigure + // Configuration Header
descLengthInterface + // CDC Interface Descriptor
descLengthInterfaceAssociation + // IAD
descCDCFuncLengthHeader + // CDC Header
descCDCFuncLengthCallManagement + // CDC Call Management Func Descriptor
descCDCFuncLengthAbstractControl + // CDC Abstract Control Func Descriptor
descCDCFuncLengthUnion + // CDC Union Functional Descriptor
descLengthEndpoint + // CDC Status IN Endpoint Descriptor
descLengthInterface + // CDC Data Interface Descriptor
descLengthEndpoint + // CDC Data IN Endpoint Descriptor
descLengthEndpoint) // CDC Data OUT Endpoint Descriptor
)
// descCDCLineCodingSize defines the length of a CDC-ACM UART line coding
// buffer. Note that the actual buffer may be padded for alignment; but for
// Rx/Tx transfer purposes, descCDCLineCodingSize defines the number of bytes
// that are transferred following a control SETUP request.
const descCDCLineCodingSize = 7
// descCDCLineCoding represents an emulated UART's line configuration.
//
// Use descCDCLineCodingSize instead of unsafe.Sizeof(descCDCLineCoding)
// in any transfer requests, because the actual struct is padded for alignment.
type descCDCLineCoding struct {
baud uint32
stopBits uint8
parity uint8
numBits uint8
_ uint8
}
// parse initializes the receiver descCDCLineCoding from the given []uint8 v.
// Argument v is a Rx transfer buffer, filled following the completion of a
// control transfer from a CDC SET_LINE_CODING (0x20) request
func (s *descCDCLineCoding) parse(v []uint8) bool {
if len(v) >= descCDCLineCodingSize {
s.baud = packU32(v[:])
s.stopBits = v[4]
s.parity = v[5]
s.numBits = v[6]
return true
}
return false
}
// descCDCLineState represents an emulated UART's line state.
type descCDCLineState struct {
// dataTerminalReady indicates if DTE is present or not.
// Corresponds to V.24 signal 108/2 and RS-232 signal DTR.
dataTerminalReady bool // DTR
// requestToSend is the carrier control for half-duplex modems.
// Corresponds to V.24 signal 105 and RS-232 signal RTS.
requestToSend bool // RTS
}
// parse initializes the receiver descCDCLineState from the given uint16 v.
// Argument v corresponds to the wValue field in a control SETUP packet, which
// carries the line state from a CDC SET_CONTROL_LINE_STATE (0x22) request.
func (s *descCDCLineState) parse(v uint16) bool {
s.dataTerminalReady = 0 != v&0x1
s.requestToSend = 0 != v&0x2
return true
}
// Common configuration constants for the USB CDC-ACM (single) device class.
const (
descCDCLanguageCount = 1 // String descriptor languages available
descCDCInterfaceCount = 2 // Interfaces for all CDC-ACM configurations.
descCDCEndpointCount = 4 // Endpoints for all CDC-ACM configurations.
descCDCEndpointCtrl = 0 // CDC-ACM Control Endpoint 0
descCDCInterfaceCtrl = 0 // CDC-ACM Control Interface
descCDCEndpointStatus = 1 // CDC-ACM Interrupt IN Endpoint
descCDCConfigAttrStatus = descEndptConfigAttrRxUnused | descEndptConfigAttrTxInterrupt
descCDCInterfaceData = 1 // CDC-ACM Data Interface
descCDCEndpointDataRx = 2 // CDC-ACM Bulk Data OUT (Rx) Endpoint
descCDCConfigAttrDataRx = descEndptConfigAttrRxBulk | descEndptConfigAttrTxUnused
descCDCEndpointDataTx = 3 // CDC-ACM Bulk Data IN (Tx) Endpoint
descCDCConfigAttrDataTx = descEndptConfigAttrRxUnused | descEndptConfigAttrTxBulk
)
// descCDCClass holds references to all descriptors, buffers, and control
// structures for the USB CDC-ACM (single) device class.
type descCDCClass struct {
*descCDCClassData // Target-defined, class-specific data
locale *[descCDCLanguageCount]descStringLanguage // string descriptors
device *[descLengthDevice]uint8 // device descriptor
qualif *[descLengthQualification]uint8 // device qualification descriptor
config *[descCDCConfigSize]uint8 // configuration descriptor
}
// descCDC holds statically-allocated instances for each of the CDC-ACM
// (single) device class configurations, ordered by index (offset by -1).
var descCDC = [dcdCount]descCDCClass{
{ // CDC-ACM (single) class configuration index 1
descCDCClassData: &descCDCData[0],
locale: &[descCDCLanguageCount]descStringLanguage{
{ // [0x0409] US English
language: descLanguageEnglish,
descriptor: descStringIndex{
{ /* [0] Language */
4,
descTypeString,
lsU8(descLanguageEnglish),
msU8(descLanguageEnglish),
},
// Actual string descriptors (index > 0) are copied into here at runtime!
// This allows for application- or even user-defined string descriptors.
{ /* [1] Manufacturer */ },
{ /* [2] Product */ },
{ /* [3] Serial Number */ },
},
},
},
device: &[descLengthDevice]uint8{
descLengthDevice, // Size of this descriptor in bytes
descTypeDevice, // Descriptor Type
lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low)
msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high)
descDeviceClassCodeMisc, // Class code (assigned by the USB-IF).
descDeviceSubClassCommon, // Subclass code (assigned by the USB-IF).
descDeviceProtocolIAD, // Protocol code (assigned by the USB-IF).
descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF)
msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF)
lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer)
msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer)
lsU8(descCommonReleaseID), // Device release number in BCD (low)
msU8(descCommonReleaseID), // Device release number in BCD (high)
1, // Index of string descriptor describing manufacturer
2, // Index of string descriptor describing product
3, // Index of string descriptor describing the device's serial number
descCDCCount, // Number of possible configurations
},
qualif: &[descLengthQualification]uint8{
descLengthQualification, // Size of this descriptor in bytes
descTypeQualification, // Descriptor Type
lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low)
msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high)
0, // Class code (assigned by the USB-IF).
0, // Subclass code (assigned by the USB-IF).
0, // Protocol code (assigned by the USB-IF).
descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
descCDCCount, // Number of possible configurations
0, // Reserved
},
config: &[descCDCConfigSize]uint8{
descLengthConfigure, // Size of this descriptor in bytes
descTypeConfigure, // Descriptor Type
lsU8(descCDCConfigSize), // Total length of data returned for this configuration (low)
msU8(descCDCConfigSize), // Total length of data returned for this configuration (high)
descCDCInterfaceCount, // Number of interfaces supported by this configuration
1, // Value to use to select this configuration (1 = CDC-ACM[0])
0, // Index of string descriptor describing this configuration
descEndptConfigAttr, // Configuration attributes
descCDCMaxPowerMa >> 1, // Max power consumption when fully-operational (2 mA units)
// Interface Association Descriptor
descLengthInterfaceAssociation, // Size of this descriptor in bytes
descTypeInterfaceAssociation, // Descriptor Type
0x00, // bFirstInterface
descCDCInterfaceCount, // bInterfaceCount
0x02, // bFunctionClass : Communications and CDC Control (0x02)
0x02, // bFunctionSubClass
0x00, // bFunctionProtocol
0x00, // iFunction
// Communication/Control Interface Descriptor
descLengthInterface, // Descriptor length
descTypeInterface, // Descriptor type
descCDCInterfaceCtrl, // Interface index
0, // Alternate setting
1, // Number of endpoints
descCDCTypeComm, // Class code
descCDCSubAbstractControl, // Subclass code
descCDCProtoAT250, // Protocol code (NOTE: Teensyduino & Arduino-Mbed define this as 1 [AT V.250])
0, // Interface Description String Index
// CDC Header Functional Descriptor
descCDCFuncLengthHeader, // Size of this descriptor in bytes
descTypeCDCInterface, // Descriptor Type
descCDCFuncTypeHeader, // Descriptor Subtype
0x10, // USB CDC specification version 1.10 (low)
0x01, // USB CDC specification version 1.10 (high)
// CDC Call Management Functional Descriptor
descCDCFuncLengthCallManagement, // Size of this descriptor in bytes
descTypeCDCInterface, // Descriptor Type
descCDCFuncTypeCallManagement, // Descriptor Subtype
0x01, // Capabilities
descCDCInterfaceData, // Data Interface
// CDC Abstract Control Management Functional Descriptor
descCDCFuncLengthAbstractControl, // Size of this descriptor in bytes
descTypeCDCInterface, // Descriptor Type
descCDCFuncTypeAbstractControl, // Descriptor Subtype
0x06, // Capabilities
// CDC Union Functional Descriptor
descCDCFuncLengthUnion, // Size of this descriptor in bytes
descTypeCDCInterface, // Descriptor Type
descCDCFuncTypeUnion, // Descriptor Subtype
descCDCInterfaceCtrl, // Controlling interface index
descCDCInterfaceData, // Controlled interface index
// Communication/Control Notification Endpoint descriptor
descLengthEndpoint, // Size of this descriptor in bytes
descTypeEndpoint, // Descriptor Type
descCDCEndpointStatus | // Endpoint address
descEndptAddrDirectionIn,
descEndptTypeInterrupt, // Attributes
lsU8(descCDCStatusPacketSize), // Max packet size (low)
msU8(descCDCStatusPacketSize), // Max packet size (high)
descCDCStatusInterval, // Polling Interval
// Data Interface Descriptor
descLengthInterface, // Interface length
descTypeInterface, // Interface type
descCDCInterfaceData, // Interface index
0, // Alternate setting
2, // Number of endpoints
descCDCTypeData, // Class code
descCDCSubNone, // Subclass code
descCDCProtoNone, // Protocol code
0, // Interface Description String Index
// Data Bulk Rx Endpoint descriptor
descLengthEndpoint, // Size of this descriptor in bytes
descTypeEndpoint, // Descriptor Type
descCDCEndpointDataRx | // Endpoint address
descEndptAddrDirectionOut,
descEndptTypeBulk, // Attributes
lsU8(descCDCDataRxPacketSize), // Max packet size (low)
msU8(descCDCDataRxPacketSize), // Max packet size (high)
0, // Polling Interval
// Data Bulk Tx Endpoint descriptor
descLengthEndpoint, // Size of this descriptor in bytes
descTypeEndpoint, // Descriptor Type
descCDCEndpointDataTx | // Endpoint address
descEndptAddrDirectionIn,
descEndptTypeBulk, // Attributes
lsU8(descCDCDataTxPacketSize), // Max packet size (low)
msU8(descCDCDataTxPacketSize), // Max packet size (high)
0, // Polling Interval
},
},
}
-244
View File
@@ -1,244 +0,0 @@
//go:build usb.cdc && (atsamd51 || atsame5x)
// +build usb.cdc
// +build atsamd51 atsame5x
package usb
import "runtime/volatile"
// descCDCCount defines the number of USB cores that may be configured as
// CDC-ACM (single) devices.
const descCDCCount = 1
// Constants for USB CDC-ACM device classes.
const (
// USB Bus Configuration Attributes
descCDCMaxPowerMa = 100 // Maximum current (mA) requested from host
// CDC-ACM Endpoint Descriptor Buffers
descCDCEDCount = descMaxEndpoints
// Setup packet is only 8 bytes in length. However, under certain scenarios,
// USB DMA controller may decide to overwrite/overflow the buffer with 2 extra
// bytes of CRC. From datasheet's "Management of SETUP Transactions" section:
// | If the number of received data bytes is the maximum data payload
// | specified by PCKSIZE.SIZE minus one, only the first CRC data is written
// | to the data buffer. If the number of received data is equal or less
// | than the data payload specified by PCKSIZE.SIZE minus two, both CRC
// | data bytes are written to the data buffer.
// Thus, we need to allocate 2 extra bytes for control endpoint 0 Rx (OUT).
descCDCSxSize = 8 + 2
descCDCCxSize = descControlPacketSize
// CDC-ACM Data Buffers
descCDCRxSize = descCDCDataRxPacketSize
descCDCTxSize = descCDCDataTxPacketSize
descCDCTxTimeoutMs = 120 // millisec
descCDCTxSyncUs = 75 // microsec
// Default CDC-ACM Endpoint Configurations (Full-Speed)
descCDCStatusInterval = descCDCStatusFSInterval // Status
descCDCStatusPacketSize = descCDCStatusFSPacketSize //
descCDCDataRxPacketSize = descCDCDataRxFSPacketSize // Data Rx
descCDCDataTxPacketSize = descCDCDataTxFSPacketSize // Data Tx
// CDC-ACM Endpoint Configurations for Full-Speed Device
descCDCStatusFSInterval = 5 // Status
descCDCStatusFSPacketSize = 64 // (full-speed)
descCDCDataRxFSPacketSize = 64 // Data Rx (full-speed)
descCDCDataTxFSPacketSize = 64 // Data Tx (full-speed)
// CDC-ACM Endpoint Configurations for High-Speed Device
// - N/A, SAMx51 only has a full-speed PHY
)
// descCDC0ED is an array of endpoint descriptors, which describes to the USB
// DMA controller the buffer and transfer properties for each endpoint, for the
// default CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDC0ED [descCDCEDCount]dhwEPAddrDesc
// descCDC0Sx is the receive (Rx) buffer for setup packets on control endpoint
// 0 of the default CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDC0Sx [descCDCSxSize]uint8
// descCDC0Cx is the transmit (Tx) buffer for control/status packets on control
// endpoint 0 of the default CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDC0Cx [descCDCCxSize]uint8
// descCDC0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0
// for the default CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDC0Dx [descCDCConfigSize]uint8
// descCDC0Rx is the receive (Rx) transfer buffer for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDC0Rx [descCDCRxSize]uint8
// descCDC0Tx is the transmit (Tx) transfer buffer for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDC0Tx [descCDCTxSize]uint8
// descCDC0Rq is the receive (Rx) transfer buffer for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDC0Rq [descCDCRxSize]uint8
// descCDC0Tq is the transmit (Tx) transfer buffer for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDC0Tq [descCDCTxSize]uint8
// descCDC0LC is the emulated UART's line coding configuration for the
// default CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDC0LC descCDCLineCoding
// descCDC0LS is the emulated UART's line state for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDC0LS descCDCLineState
// descCDCState defines the state of the CDC-ACM handshake initialization.
//
// Many USB hosts will send a default SET_LINE_CODING prior to SET_LINE_STATE,
// and then another SET_LINE_CODING containing the actual terminal settings.
//
// We do not want to start UART Rx/Tx transactions until after we have
// received the final SET_LINE_CODING with the intended terminal settings.
// Otherwise, the host may cancel any data transfers occurring during a change
// in line state or line coding.
//
// The "set" method on type descCDCState defines this incremental state
// machine, with the UART's current state stored in the volatile.Register8
// field "st" of descCDCClassData.
type descCDCState uint8
// set implements the state transition logic described in the godoc comment on
// type descCDCState. Returns the value of the resulting state.
//go:inline
func (s *descCDCState) set(state descCDCState) descCDCState {
if state > *s {
// state must be incremented in-order. Otherwise, reset to initial state.
if state == *s+1 {
*s = state
} else {
var init descCDCState // Reset to zero-value of type.
*s = init
}
}
// Return a value for safely chaining the result.
// (Not a pointer to the object we just modified.)
return *s
}
const (
descCDCStateConfigured descCDCState = iota // Received SET_CONFIGURATION class request
descCDCStateLineState // Received SET_LINE_STATE after Configured state
descCDCStateLineCoding // Received SET_LINE_CODING after LineState state
)
// descCDCClassData holds the buffers and control states for all CDC-ACM
// (single) device class configurations, ordered by index (offset by -1), for
// SAMx51 targets only.
//
// Instances of this type (elements of descCDCData) are embedded in elements
// of the common/target-agnostic CDC-ACM class configurations (descCDC).
// Methods defined on this type implement target-specific functionality, and
// some of these methods are required by the common device controller driver.
// Thus, this type functions as a hardware abstraction layer (HAL).
type descCDCClassData struct {
// CDC-ACM Control Buffers
ed *[descCDCEDCount]dhwEPAddrDesc // endpoint descriptors
sx *[descCDCSxSize]uint8 // control endpoint 0 Rx (OUT) setup packets
cx *[descCDCCxSize]uint8 // control endpoint 0 Tx (IN) control/status packets
dx *[descCDCConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
// CDC-ACM Data Buffers
rx *[descCDCRxSize]uint8 // bulk data endpoint Rx (OUT) transfer buffer
tx *[descCDCTxSize]uint8 // bulk data endpoint Tx (IN) transfer buffer
rxq *[descCDCRxSize]uint8 // CDC-ACM UART Rx FIFO
txq *[descCDCTxSize]uint8 // CDC-ACM UART Tx FIFO
rq *Queue // CDC-ACM UART Rx Queue (backed by FIFO rxq)
tq *Queue // CDC-ACM UART Tx Queue (backed by FIFO txq)
lc *descCDCLineCoding // UART line coding
ls *descCDCLineState // UART line state
st volatile.Register8
sxSize uint32
rxSize uint32
txSize uint32
}
// setState is a wrapper for converting and storing the given descCDCState
// value as a uint8 in the receiver's volatile.Register8 field st.
//go:inline
func (c *descCDCClassData) setState(state descCDCState) {
s := descCDCState(c.st.Get())
c.st.Set(uint8(s.set(state)))
}
// state is a wrapper for retrieving and converting the receiver's
// volatile.Register8 field st from uint8 to descCDCState.
//go:inline
func (c *descCDCClassData) state() descCDCState {
return descCDCState(c.st.Get())
}
// descCDCData holds statically-allocated instances for each of the target-
// specific (SAMx51) CDC-ACM (single) device class configurations' control and
// data structures, ordered by configuration index (offset by -1). Each element
// is embedded in a corresponding element of descCDC.
var descCDCData = [dcdCount]descCDCClassData{
{ // -- CDC-ACM (single) Class Configuration Index 1 --
// CDC-ACM Control Buffers
ed: &descCDC0ED,
sx: &descCDC0Sx,
cx: &descCDC0Cx,
dx: &descCDC0Dx,
// CDC-ACM Data Buffers
rx: &descCDC0Rx,
tx: &descCDC0Tx,
rxq: &descCDC0Rq,
txq: &descCDC0Tq,
rq: &Queue{},
tq: &Queue{},
lc: &descCDC0LC,
ls: &descCDC0LS,
sxSize: descCDCStatusPacketSize,
rxSize: descCDCDataRxPacketSize,
txSize: descCDCDataTxPacketSize,
},
}
-515
View File
@@ -1,515 +0,0 @@
//go:build usb.hid
// +build usb.hid
package usb
// descCDCCount defines the number of USB cores that may be configured as
// CDC-ACM (single) devices.
const descCDCCount = 0
// USB HID constants defined per specification
const (
// HID class
descHIDType = 0x03
// HID subclass
descHIDSubNone = 0x00
descHIDSubBoot = 0x01
// HID protocol
descHIDProtoNone = 0x00
descHIDProtoKeyboard = 0x01
descHIDProtoMouse = 0x02
descHIDRequestGetReport = 0x01 // HID request GET_REPORT
descHIDRequestGetReportTypeInput = 0x01 // HID request GET_REPORT type INPUT
descHIDRequestGetReportTypeOutput = 0x02 // HID request GET_REPORT type OUTPUT
descHIDRequestGetReportTypeFeature = 0x03 // HID request GET_REPORT type FEATURE
descHIDRequestGetIdle = 0x02 // HID request GET_IDLE
descHIDRequestGetProtocol = 0x03 // HID request GET_PROTOCOL
descHIDRequestSetReport = 0x09 // HID request SET_REPORT
descHIDRequestSetIdle = 0x0A // HID request SET_IDLE
descHIDRequestSetProtocol = 0x0B // HID request SET_PROTOCOL
)
const (
// Size of all HID configuration descriptors.
descHIDConfigSize = uint16(
descLengthConfigure + // Configuration Header
descLengthInterface + // Keyboard Interface Descriptor
descLengthInterface + // Keyboard HID Interface Descriptor
descLengthEndpoint + // Keyboard Endpoint Descriptor
descLengthInterface + // Mouse Interface Descriptor
descLengthInterface + // Mouse HID Interface Descriptor
descLengthEndpoint + // Mouse Endpoint Descriptor
descLengthInterface + // Serial Interface Descriptor
descLengthInterface + // Serial HID Interface Descriptor
descLengthEndpoint + // Serial Tx Endpoint Descriptor
descLengthEndpoint + // Serial Rx Endpoint Descriptor
descLengthInterface + // Joystick Interface Descriptor
descLengthInterface + // Joystick HID Interface Descriptor
descLengthEndpoint + // Joystick Endpoint Descriptor
descLengthInterface + // Keyboard Media Keys Interface Descriptor
descLengthInterface + // Keyboard Media Keys HID Interface Descriptor
descLengthEndpoint) // Keyboard Media Keys Endpoint Descriptor
// Position of each HID interface descriptor as offsets into the configuration
// descriptor. See comments in the configuration descriptor definition for the
// incremental tally that computes these.
descHIDConfigKeyboardPos = 18
descHIDConfigMousePos = 43
descHIDConfigSerialPos = 68
descHIDConfigJoystickPos = 100
descHIDConfigMediaKeyPos = 125
)
// Common configuration constants for the USB HID device class.
const (
descHIDLanguageCount = 1 // String descriptor languages available
descHIDInterfaceCount = 5 // Interfaces for all HID configurations.
descHIDEndpointCount = 6 // Endpoints for all HID configurations.
descHIDEndpointCtrl = 0 // HID Control Endpoint 0
descHIDInterfaceKeyboard = 0 // HID Keyboard Interface
descHIDEndpointKeyboard = 3 // HID Keyboard IN Endpoint
descHIDConfigAttrKeyboard = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused
descHIDInterfaceMouse = 1 // HID Mouse Interface
descHIDEndpointMouse = 5 // HID Mouse IN Endpoint
descHIDConfigAttrMouse = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused
descHIDInterfaceSerial = 2 // HID Serial (UART emulation) Interface
descHIDEndpointSerialRx = 2 // HID Serial OUT (Rx) Endpoint
descHIDEndpointSerialTx = 2 // HID Serial IN (Tx) Endpoint
descHIDConfigAttrSerial = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxInterrupt
descHIDInterfaceJoystick = 3 // HID Joystick Interface
descHIDEndpointJoystick = 6 // HID Joystick IN Endpoint
descHIDConfigAttrJoystick = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused
descHIDInterfaceMediaKey = 4 // HID Keyboard Media Keys Interface
descHIDEndpointMediaKey = 4 // HID Keyboard Media Keys IN Endpoint
descHIDConfigAttrMediaKey = descEndptConfigAttrTxInterrupt | descEndptConfigAttrRxUnused
)
// descHIDClass holds references to all descriptors, buffers, and control
// structures for the USB HID device class.
type descHIDClass struct {
*descHIDClassData // Target-defined, class-specific data
locale *[descHIDLanguageCount]descStringLanguage // string descriptors
device *[descLengthDevice]uint8 // device descriptor
qualif *[descLengthQualification]uint8 // device qualification descriptor
config *[descHIDConfigSize]uint8 // configuration descriptor
}
// descHID holds statically-allocated instances for each of the HID device class
// configurations, ordered by index (offset by -1).
var descHID = [dcdCount]descHIDClass{
{ // HID class configuration index 1
descHIDClassData: &descHIDData[0],
locale: &[descHIDLanguageCount]descStringLanguage{
{ // [0x0409] US English
language: descLanguageEnglish,
descriptor: descStringIndex{
{ /* [0] Language */
4,
descTypeString,
lsU8(descLanguageEnglish),
msU8(descLanguageEnglish),
},
// Actual string descriptors (index > 0) are copied into here at runtime!
// This allows for application- or even user-defined string descriptors.
{ /* [1] Manufacturer */ },
{ /* [2] Product */ },
{ /* [3] Serial Number */ },
},
},
},
device: &[descLengthDevice]uint8{
descLengthDevice, // Size of this descriptor in bytes
descTypeDevice, // Descriptor Type
lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low)
msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high)
0, // Class code (assigned by the USB-IF).
0, // Subclass code (assigned by the USB-IF).
0, // Protocol code (assigned by the USB-IF).
descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF)
msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF)
lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer)
msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer)
lsU8(descCommonReleaseID), // Device release number in BCD (low)
msU8(descCommonReleaseID), // Device release number in BCD (high)
1, // Index of string descriptor describing manufacturer
2, // Index of string descriptor describing product
3, // Index of string descriptor describing the device's serial number
descHIDCount, // Number of possible configurations
},
qualif: &[descLengthQualification]uint8{
descLengthQualification, // Size of this descriptor in bytes
descTypeQualification, // Descriptor Type
lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low)
msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high)
0, // Class code (assigned by the USB-IF).
0, // Subclass code (assigned by the USB-IF).
0, // Protocol code (assigned by the USB-IF).
descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
descHIDCount, // Number of possible configurations
0, // Reserved
},
config: &[descHIDConfigSize]uint8{
// [0+9]
descLengthConfigure, // Size of this descriptor in bytes
descTypeConfigure, // Descriptor Type
lsU8(descHIDConfigSize), // Total length of data returned for this configuration (low)
msU8(descHIDConfigSize), // Total length of data returned for this configuration (high)
descHIDInterfaceCount, // Number of interfaces supported by this configuration
1, // Value to use to select this configuration (1 = CDC-ACM[0])
0, // Index of string descriptor describing this configuration
descEndptConfigAttr, // Configuration attributes
descHIDMaxPowerMa >> 1, // Max power consumption when fully-operational (2 mA units)
// [9+9] Keyboard Interface Descriptor
descLengthInterface, // Descriptor length
descTypeInterface, // Descriptor type
descHIDInterfaceKeyboard, // Interface index
0, // Alternate setting
1, // Number of endpoints
descHIDType, // Class code (HID = 0x03)
descHIDSubBoot, // Subclass code (Boot = 0x01)
descHIDProtoKeyboard, // Protocol code (Keyboard = 0x01)
0, // Interface Description String Index
// [18+9] Keyboard HID Interface Descriptor
descLengthInterface, // Descriptor length
descTypeHID, // Descriptor type
0x11, // HID BCD (low)
0x01, // HID BCD (high)
0, // Country code
1, // Number of descriptors
descTypeHIDReport, // Descriptor type
lsU8(uint16(len(descHIDReportKeyboard))), // Descriptor length (low)
msU8(uint16(len(descHIDReportKeyboard))), // Descriptor length (high)
// [27+7] Keyboard Endpoint Descriptor
descLengthEndpoint, // Size of this descriptor in bytes
descTypeEndpoint, // Descriptor Type
descHIDEndpointKeyboard | // Endpoint address
descEndptAddrDirectionIn,
descEndptTypeInterrupt, // Attributes
lsU8(descHIDKeyboardTxPacketSize), // Max packet size (low)
msU8(descHIDKeyboardTxPacketSize), // Max packet size (high)
descHIDKeyboardTxInterval, // Polling Interval
// [34+9] Mouse Interface Descriptor
descLengthInterface, // Descriptor length
descTypeInterface, // Descriptor type
descHIDInterfaceMouse, // Interface index
0, // Alternate setting
1, // Number of endpoints
descHIDType, // Class code (HID = 0x03)
descHIDSubBoot, // Subclass code (Boot = 0x01)
descHIDProtoMouse, // Protocol code (Mouse = 0x02)
0, // Interface Description String Index
// [43+9] Mouse HID Interface Descriptor
descLengthInterface, // Descriptor length
descTypeHID, // Descriptor type
0x11, // HID BCD (low)
0x01, // HID BCD (high)
0, // Country code
1, // Number of descriptors
descTypeHIDReport, // Descriptor type
lsU8(uint16(len(descHIDReportMouse))), // Descriptor length (low)
msU8(uint16(len(descHIDReportMouse))), // Descriptor length (high)
// [52+7] Mouse Endpoint Descriptor
descLengthEndpoint, // Size of this descriptor in bytes
descTypeEndpoint, // Descriptor Type
descHIDEndpointMouse | // Endpoint address
descEndptAddrDirectionIn,
descEndptTypeInterrupt, // Attributes
lsU8(descHIDMouseTxPacketSize), // Max packet size (low)
msU8(descHIDMouseTxPacketSize), // Max packet size (high)
descHIDMouseTxInterval, // Polling Interval
// [59+9] Serial Interface Descriptor
descLengthInterface, // Descriptor length
descTypeInterface, // Descriptor type
descHIDInterfaceSerial, // Interface index
0, // Alternate setting
2, // Number of endpoints
descHIDType, // Class code (HID = 0x03)
descHIDSubNone, // Subclass code
descHIDProtoNone, // Protocol code
0, // Interface Description String Index
// [68+9] Serial HID Interface Descriptor
descLengthInterface, // Descriptor length
descTypeHID, // Descriptor type
0x11, // HID BCD (low)
0x01, // HID BCD (high)
0, // Country code
1, // Number of descriptors
descTypeHIDReport, // Descriptor type
lsU8(uint16(len(descHIDReportSerial))), // Descriptor length (low)
msU8(uint16(len(descHIDReportSerial))), // Descriptor length (high)
// [77+7] Serial Tx Endpoint Descriptor
descLengthEndpoint, // Size of this descriptor in bytes
descTypeEndpoint, // Descriptor Type
descHIDEndpointSerialTx | // Endpoint address
descEndptAddrDirectionIn,
descEndptTypeInterrupt, // Attributes
lsU8(descHIDSerialTxPacketSize), // Max packet size (low)
msU8(descHIDSerialTxPacketSize), // Max packet size (high)
descHIDSerialTxInterval, // Polling Interval
// [84+7] Serial Rx Endpoint Descriptor
descLengthEndpoint, // Size of this descriptor in bytes
descTypeEndpoint, // Descriptor Type
descHIDEndpointSerialRx | // Endpoint address
descEndptAddrDirectionOut,
descEndptTypeInterrupt, // Attributes
lsU8(descHIDSerialRxPacketSize), // Max packet size (low)
msU8(descHIDSerialRxPacketSize), // Max packet size (high)
descHIDSerialRxInterval, // Polling Interval
// [91+9] Joystick Interface Descriptor
descLengthInterface, // Descriptor length
descTypeInterface, // Descriptor type
descHIDInterfaceJoystick, // Interface index
0, // Alternate setting
1, // Number of endpoints
descHIDType, // Class code (HID = 0x03)
descHIDSubNone, // Subclass code
descHIDProtoNone, // Protocol code
0, // Interface Description String Index
// [100+9] Joystick HID Interface Descriptor
descLengthInterface, // Descriptor length
descTypeHID, // Descriptor type
0x11, // HID BCD (low)
0x01, // HID BCD (high)
0, // Country code
1, // Number of descriptors
descTypeHIDReport, // Descriptor type
lsU8(uint16(len(descHIDReportJoystick))), // Descriptor length (low)
msU8(uint16(len(descHIDReportJoystick))), // Descriptor length (high)
// [109+7] Joystick Endpoint Descriptor
descLengthEndpoint, // Size of this descriptor in bytes
descTypeEndpoint, // Descriptor Type
descHIDEndpointJoystick | // Endpoint address
descEndptAddrDirectionIn,
descEndptTypeInterrupt, // Attributes
lsU8(descHIDJoystickTxPacketSize), // Max packet size (low)
msU8(descHIDJoystickTxPacketSize), // Max packet size (high)
descHIDJoystickTxInterval, // Polling Interval
// [116+9] Keyboard Media Keys Interface Descriptor
descLengthInterface, // Descriptor length
descTypeInterface, // Descriptor type
descHIDInterfaceMediaKey, // Interface index
0, // Alternate setting
1, // Number of endpoints
descHIDType, // Class code (HID = 0x03)
descHIDSubNone, // Subclass code
descHIDProtoNone, // Protocol code
0, // Interface Description String Index
// [125+9] Keyboard Media Keys HID Interface Descriptor
descLengthInterface, // Descriptor length
descTypeHID, // Descriptor type
0x11, // HID BCD (low)
0x01, // HID BCD (high)
0, // Country code
1, // Number of descriptors
descTypeHIDReport, // Descriptor type
lsU8(uint16(len(descHIDReportMediaKey))), // Descriptor length (low)
msU8(uint16(len(descHIDReportMediaKey))), // Descriptor length (high)
// [134+7] Keyboard Media Keys Endpoint Descriptor
descLengthEndpoint, // Size of this descriptor in bytes
descTypeEndpoint, // Descriptor Type
descHIDEndpointMediaKey | // Endpoint address
descEndptAddrDirectionIn,
descEndptTypeInterrupt, // Attributes
lsU8(descHIDMediaKeyTxPacketSize), // Max packet size (low)
msU8(descHIDMediaKeyTxPacketSize), // Max packet size (high)
descHIDMediaKeyTxInterval, // Polling Interval
},
},
}
var descHIDReportSerial = [...]uint8{
0x06, 0xC9, 0xFF, // Usage Page 0xFFC9 (vendor defined)
0x09, 0x04, // Usage 0x04
0xA1, 0x5C, // Collection 0x5C
0x75, 0x08, // report size = 8 bits (global)
0x15, 0x00, // logical minimum = 0 (global)
0x26, 0xFF, 0x00, // logical maximum = 255 (global)
0x95, descHIDSerialTxPacketSize, // report count (global)
0x09, 0x75, // usage (local)
0x81, 0x02, // Input
0x95, descHIDSerialRxPacketSize, // report count (global)
0x09, 0x76, // usage (local)
0x91, 0x02, // Output
0x95, 0x04, // report count (global)
0x09, 0x76, // usage (local)
0xB1, 0x02, // Feature
0xC0, // end collection
}
var descHIDReportKeyboard = [...]uint8{
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0xE0, // Usage Minimum (224)
0x29, 0xE7, // Usage Maximum (231)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x81, 0x02, // Input (Data, Variable, Absolute) [Modifier keys]
0x95, 0x01, // Report Count (1)
0x75, 0x08, // Report Size (8)
0x81, 0x03, // Input (Constant) [Reserved byte]
0x95, 0x05, // Report Count (5)
0x75, 0x01, // Report Size (1)
0x05, 0x08, // Usage Page (LEDs)
0x19, 0x01, // Usage Minimum (1)
0x29, 0x05, // Usage Maximum (5)
0x91, 0x02, // Output (Data, Variable, Absolute) [LED report]
0x95, 0x01, // Report Count (1)
0x75, 0x03, // Report Size (3)
0x91, 0x03, // Output (Constant) [LED report padding]
0x95, 0x06, // Report Count (6)
0x75, 0x08, // Report Size (8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x7F, // Logical Maximum(104)
0x05, 0x07, // Usage Page (Key Codes)
0x19, 0x00, // Usage Minimum (0)
0x29, 0x7F, // Usage Maximum (104)
0x81, 0x00, // Input (Data, Array) [Normal keys]
0xC0, // End Collection
}
var descHIDReportMediaKey = [...]uint8{
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Controls)
0xA1, 0x01, // Collection (Application)
0x75, 0x0A, // Report Size (10)
0x95, 0x04, // Report Count (4)
0x19, 0x00, // Usage Minimum (0)
0x2A, 0x9C, 0x02, // Usage Maximum (0x29C)
0x15, 0x00, // Logical Minimum (0)
0x26, 0x9C, 0x02, // Logical Maximum (0x29C)
0x81, 0x00, // Input (Data, Array)
0x05, 0x01, // Usage Page (Generic Desktop)
0x75, 0x08, // Report Size (8)
0x95, 0x03, // Report Count (3)
0x19, 0x00, // Usage Minimum (0)
0x29, 0xB7, // Usage Maximum (0xB7)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xB7, 0x00, // Logical Maximum (0xB7)
0x81, 0x00, // Input (Data, Array)
0xC0, // End Collection
}
var descHIDReportMouse = [...]uint8{
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // REPORT_ID (1)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (Button #1)
0x29, 0x08, // Usage Maximum (Button #8)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x95, 0x08, // Report Count (8)
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data, Variable, Absolute)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x38, // Usage (Wheel)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x75, 0x08, // Report Size (8),
0x95, 0x03, // Report Count (3),
0x81, 0x06, // Input (Data, Variable, Relative)
0x05, 0x0C, // Usage Page (Consumer)
0x0A, 0x38, 0x02, // Usage (AC Pan)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x75, 0x08, // Report Size (8),
0x95, 0x01, // Report Count (1),
0x81, 0x06, // Input (Data, Variable, Relative)
0xC0, // End Collection
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
0x85, 0x02, // REPORT_ID (2)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x7F, // Logical Maximum (32767)
0x75, 0x10, // Report Size (16),
0x95, 0x02, // Report Count (2),
0x81, 0x02, // Input (Data, Variable, Absolute)
0xC0, // End Collection
}
var descHIDReportJoystick = [...]uint8{
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x20, // Report Count (32)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (Button #1)
0x29, 0x20, // Usage Maximum (Button #32)
0x81, 0x02, // Input (variable,absolute)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x07, // Logical Maximum (7)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x65, 0x14, // Unit (20)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (variable,absolute,null_state)
0x05, 0x01, // Usage Page (Generic Desktop)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection ()
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x03, // Logical Maximum (1023)
0x75, 0x0A, // Report Size (10)
0x95, 0x04, // Report Count (4)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x81, 0x02, // Input (variable,absolute)
0xC0, // End Collection
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x03, // Logical Maximum (1023)
0x75, 0x0A, // Report Size (10)
0x95, 0x02, // Report Count (2)
0x09, 0x36, // Usage (Slider)
0x09, 0x36, // Usage (Slider)
0x81, 0x02, // Input (variable,absolute)
0xC0, // End Collection
}
-274
View File
@@ -1,274 +0,0 @@
//go:build usb.hid && (atsamd51 || atsame5x)
// +build usb.hid
// +build atsamd51 atsame5x
package usb
// descHIDCount defines the number of USB cores that may be configured as a
// composite (keyboard + mouse + joystick) human interface device (HID).
const descHIDCount = 1
// Constants for USB HID (keyboard, mouse, joystick) device classes.
const (
// USB Bus Configuration Attributes
descHIDMaxPowerMa = 100 // Maximum current (mA) requested from host
// HID Endpoint Descriptor Buffers
descHIDEDCount = descMaxEndpoints
// Setup packet is only 8 bytes in length. However, under certain scenarios,
// USB DMA controller may decide to overwrite/overflow the buffer with 2 extra
// bytes of CRC. From datasheet's "Management of SETUP Transactions" section:
// | If the number of received data bytes is the maximum data payload
// | specified by PCKSIZE.SIZE minus one, only the first CRC data is written
// | to the data buffer. If the number of received data is equal or less
// | than the data payload specified by PCKSIZE.SIZE minus two, both CRC
// | data bytes are written to the data buffer.
// Thus, we need to allocate 2 extra bytes for control endpoint 0 Rx (OUT).
descHIDSxSize = 8 + 2
descHIDCxSize = descControlPacketSize
// HID Serial Buffers
descHIDSerialRxSize = descHIDSerialRxPacketSize
descHIDSerialTxSize = descHIDSerialTxPacketSize
descHIDSerialTxTimeoutMs = 50 // millisec
descHIDSerialTxSyncUs = 75 // microsec
// HID Keyboard Buffers
descHIDKeyboardTxSize = 4 * descHIDKeyboardTxPacketSize
descHIDKeyboardTxTimeoutMs = 50 // millisec
// HID Mouse Buffers
descHIDMouseTxSize = 4 * descHIDMouseTxPacketSize
descHIDMouseTxTimeoutMs = 30 // millisec
// HID Joystick Buffers
descHIDJoystickTxSize = 4 * descHIDJoystickTxPacketSize
descHIDJoystickTxTimeoutMs = 30 // millisec
// Default HID Endpoint Configurations (Full-Speed)
descHIDSerialRxInterval = descHIDSerialRxFSInterval // Serial Rx
descHIDSerialRxPacketSize = descHIDSerialRxFSPacketSize //
descHIDSerialTxInterval = descHIDSerialTxFSInterval // Serial Tx
descHIDSerialTxPacketSize = descHIDSerialTxFSPacketSize //
descHIDKeyboardTxInterval = descHIDKeyboardTxFSInterval // Keyboard
descHIDKeyboardTxPacketSize = descHIDKeyboardTxFSPacketSize //
descHIDMediaKeyTxInterval = descHIDMediaKeyTxFSInterval // Keyboard Media Keys
descHIDMediaKeyTxPacketSize = descHIDMediaKeyTxFSPacketSize //
descHIDMouseTxInterval = descHIDMouseTxFSInterval // Mouse
descHIDMouseTxPacketSize = descHIDMouseTxFSPacketSize //
descHIDJoystickTxInterval = descHIDJoystickTxFSInterval // Joystick
descHIDJoystickTxPacketSize = descHIDJoystickTxFSPacketSize //
// HID Endpoint Configurations for Full-Speed Device
descHIDSerialRxFSInterval = 2 // Serial Rx
descHIDSerialRxFSPacketSize = 8 // (full-speed)
descHIDSerialTxFSInterval = 1 // Serial Tx
descHIDSerialTxFSPacketSize = 16 // (full-speed)
descHIDKeyboardTxFSInterval = 4 // Keyboard
descHIDKeyboardTxFSPacketSize = 8 // (full-speed)
descHIDMediaKeyTxFSInterval = 4 // Keyboard Media Keys
descHIDMediaKeyTxFSPacketSize = 8 // (full-speed)
descHIDMouseTxFSInterval = 4 // Mouse
descHIDMouseTxFSPacketSize = 8 // (full-speed)
descHIDJoystickTxFSInterval = 4 // Joystick
descHIDJoystickTxFSPacketSize = 12 // (full-speed)
// HID Endpoint Configurations for High-Speed Device
// - N/A, SAMx51 only has a full-speed PHY
)
// descHID0ED is an array of endpoint descriptors, which describes to the USB
// DMA controller the buffer and transfer properties for each endpoint, for the
// default HID device class configuration (index 1).
//go:align 32
var descHID0ED [descHIDEDCount]dhwEPAddrDesc
// descHID0Sx is the receive (Rx) buffer for setup packets on control endpoint 0
// of the default HID device class configuration (index 1).
//go:align 32
var descHID0Sx [descHIDSxSize]uint8
// descHID0Cx is the transmit (Tx) buffer for control/status packets on control
// endpoint 0 of the default CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descHID0Cx [descHIDCxSize]uint8
// descHID0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 for
// the default HID device class configuration (index 1).
//go:align 32
var descHID0Dx [descHIDConfigSize]uint8
// descHID0SerialRx is the serial receive (Rx) transfer buffer for the default
// HID device class configuration (index 1).
//go:align 32
// var descHID0SerialRx [descHIDSerialRxSize]uint8
// descHID0SerialTx is the serial transmit (Tx) transfer buffer for the default
// HID device class configuration (index 1).
//go:align 32
// var descHID0SerialTx [descHIDSerialTxSize]uint8
// descHID0KeyboardTx is the keyboard HID report transmit (Tx) transfer buffer
// for the default HID device class configuration (index 1).
//go:align 32
var descHID0KeyboardTx [descHIDKeyboardTxPacketSize]uint8
// descHID0KeyboardTq is the keyboard transmit (Tx) transfer buffer for the
// default HID device class configuration (index 1).
//go:align 32
// var descHID0KeyboardTq [descHIDKeyboardTxSize]uint8
// descHID0MouseTx is the mouse transmit (Tx) transfer buffer for the default
// HID device class configuration (index 1).
//go:align 32
// var descHID0MouseTx [descHIDMouseTxSize]uint8
// descHID0JoystickTx is the joystick transmit (Tx) transfer buffer for the
// default HID device class configuration (index 1).
//go:align 32
// var descHID0JoystickTx [descHIDJoystickTxSize]uint8
var descHID0KeyboardTxKey [hidKeyboardKeyCount]uint8
var descHID0KeyboardTxCon [hidKeyboardConCount]uint16
var descHID0KeyboardTxSys [hidKeyboardSysCount]uint8
// descHID0Keyboard is the Keyboard instance with which the user may interact
// when using the default HID device class configuration (index 1).
var descHID0Keyboard = Keyboard{
key: &descHID0KeyboardTxKey,
con: &descHID0KeyboardTxCon,
sys: &descHID0KeyboardTxSys,
}
// descHIDClassData holds the buffers and control states for all of the HID
// device class configurations, ordered by index (offset by -1), for SAMx51
// targets only.
//
// Instances of this type (elements of descHIDData) are embedded in elements
// of the common/target-agnostic HID class configurations (descHID).
// Methods defined on this type implement target-specific functionality, and
// some of these methods are required by the common device controller driver.
// Thus, this type functions as a hardware abstraction layer (HAL).
type descHIDClassData struct {
// HID Control Buffers
ed *[descHIDEDCount]dhwEPAddrDesc // endpoint descriptors
sx *[descHIDSxSize]uint8 // control endpoint 0 Rx (OUT) setup packets
cx *[descHIDCxSize]uint8 // control endpoint 0 Tx (IN) control/status packets
dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
// HID Serial Buffers
// rxSerial *[descHIDSerialRxSize]uint8 // interrupt endpoint serial Rx (OUT) transfer buffer
// txSerial *[descHIDSerialTxSize]uint8 // interrupt endpoint serial Tx (IN) transfer buffer
// rxSerialSize uint16
// txSerialSize uint16
// HID Keyboard Buffers
txKeyboard *[descHIDKeyboardTxPacketSize]uint8 // interrupt endpoint keyboard Tx (IN) HID report buffer
// txqKeyboard *[descHIDKeyboardTxSize]uint8 // interrupt endpoint keyboard Tx (IN) transfer FIFO
// tqKeyboard *Queue
txKeyboardSize uint16
// HID Mouse Buffers
// txMouse *[descHIDMouseTxSize]uint8 // interrupt endpoint mouse Tx (IN) transfer buffer
// txMouseSize uint16
// HID Joystick Buffers
// txJoystick *[descHIDJoystickTxSize]uint8 // interrupt endpoint joystick Tx (IN) transfer buffer
// txJoystickSize uint16
// HID Device Instances
//serial *Serial
keyboard *Keyboard
//mouse *Mouse
//joystick *Joystick
}
// descHIDData holds statically-allocated instances for each of the target-
// specific (SAMx51) HID device class configurations' control and data
// structures, ordered by configuration index (offset by -1). Each element is
// embedded in a corresponding element of descHID.
var descHIDData = [dcdCount]descHIDClassData{
{ // -- HID Class Configuration Index 1 --
// HID Control Buffers
ed: &descHID0ED,
sx: &descHID0Sx,
cx: &descHID0Cx,
dx: &descHID0Dx,
// HID Serial Buffers
// rxSerial: &descHID0SerialRx,
// txSerial: &descHID0SerialTx,
// rxSerialSize: descHIDSerialRxPacketSize,
// txSerialSize: descHIDSerialTxPacketSize,
// HID Keyboard Buffers
txKeyboard: &descHID0KeyboardTx,
// txqKeyboard: &descHID0KeyboardTq,
// tqKeyboard: &Queue{},
txKeyboardSize: descHIDKeyboardTxPacketSize,
// HID Mouse Buffers
// txMouse: &descHID0MouseTx,
// txMouseSize: descHIDMouseTxPacketSize,
// HID Joystick Buffers
// txJoystick: &descHID0JoystickTx,
// txJoystickSize: descHIDJoystickTxPacketSize,
// HID Device Instances
//serial: &descHID0Serial,
keyboard: &descHID0Keyboard,
//mouse: &descHID0Mouse,
//joystick: &descHID0Joystick,
},
}
-221
View File
@@ -1,221 +0,0 @@
package usb
const descUSBSpecVersion = uint16(0x0200) // USB 2.0
const descLanguageEnglish = uint16(0x0409) // (US) English
// USB constants defined per specification.
const (
// Descriptor length
descLengthDevice = 18
descLengthConfigure = 9
descLengthInterface = 9
descLengthInterfaceAssociation = 8
descLengthEndpoint = 7
descLengthQualification = 10
descLengthOTG = 5
descLengthBOS = 5
descLengthEndpointCompanion = 6
descLengthUSB20Extension = 7
descLengthSuperspeed = 10
// Descriptor type
descTypeDevice = 0x01
descTypeConfigure = 0x02
descTypeString = 0x03
descTypeInterface = 0x04
descTypeEndpoint = 0x05
descTypeQualification = 0x06
descTypeOtherSpeedConfiguration = 0x07
descTypeInterfacePower = 0x08
descTypeOTG = 0x09
descTypeInterfaceAssociation = 0x0B
descTypeBOS = 0x0F
descTypeDeviceCapability = 0x10
descTypeHID = 0x21
descTypeHIDReport = 0x22
descTypeHIDPhysical = 0x23
descTypeCDCInterface = 0x24
descTypeCDCEndpoint = 0x25
descTypeEndpointCompanion = 0x30
// Standard request type
descRequestTypeDirMsk = 0x80
descRequestTypeDirPos = 7
descRequestTypeDirOut = 0x00
descRequestTypeDirIn = 0x80
descRequestTypeTypeMsk = 0x60
descRequestTypeTypePos = 5
descRequestTypeTypeStandard = 0
descRequestTypeTypeClass = 0x20
descRequestTypeTypeVendor = 0x40
descRequestTypeRecipientMsk = 0x1F
descRequestTypeRecipientPos = 0
descRequestTypeRecipientDevice = 0x00
descRequestTypeRecipientInterface = 0x01
descRequestTypeRecipientEndpoint = 0x02
descRequestTypeRecipientOther = 0x03
// Standard request
descRequestStandardGetStatus = 0x00
descRequestStandardClearFeature = 0x01
descRequestStandardSetFeature = 0x03
descRequestStandardSetAddress = 0x05
descRequestStandardGetDescriptor = 0x06
descRequestStandardSetDescriptor = 0x07
descRequestStandardGetConfiguration = 0x08
descRequestStandardSetConfiguration = 0x09
descRequestStandardGetInterface = 0x0A
descRequestStandardSetInterface = 0x0B
descRequestStandardSynchFrame = 0x0C
// Configuration attributes
descConfigAttrD7Msk = 0x80
descConfigAttrD7Pos = 7
descConfigAttrSelfPoweredMsk = 0x40
descConfigAttrSelfPoweredPos = 6
descConfigAttrRemoteWakeupMsk = 0x20
descConfigAttrRemoteWakeupPos = 5
// Endpoint type
descEndptTypeControl = 0x00
descEndptTypeIsochronous = 0x01
descEndptTypeBulk = 0x02
descEndptTypeInterrupt = 0x03
// Endpoint address
descEndptAddrNumberMsk = 0x0F
descEndptAddrNumberPos = 0
descEndptAddrDirectionMsk = 0x80
descEndptAddrDirectionPos = 7
descEndptAddrDirectionOut = 0
descEndptAddrDirectionIn = 0x80
// Endpoint attributes
descEndptAttrTypeMsk = 0x03
descEndptAttrNumberPos = 0
descEndptAttrSyncTypeMsk = 0x0C
descEndptAttrSyncTypePos = 2
descEndptAttrSyncTypeNoSync = 0x00
descEndptAttrSyncTypeAsync = 0x04
descEndptAttrSyncTypeAdaptive = 0x08
descEndptAttrSyncTypeSync = 0x0C
descEndptAttrUsageTypeMsk = 0x30
descEndptAttrUsageTypePos = 4
descEndptAttrUsageTypeData = 0x00
descEndptAttrUsageTypeFeed = 0x10
descEndptAttrUsageTypeFeedData = 0x20
// Endpoint max packet size
descEndptMaxPktSizeMsk = 0x07FF
descEndptMaxPktSize = 64
descEndptMaxPktSizeMultMsk = 0x1800
descEndptMaxPktSizeMultPos = 11
// OTG attributes
descOTGAttrSRPMsk = 0x01
descOTGAttrHNPMsk = 0x02
descOTGAttrADPMsk = 0x04
// Device bus speed
descDeviceSpeedFull = 0x00
descDeviceSpeedLow = 0x01
descDeviceSpeedHigh = 0x02
descDeviceSpeedSuper = 0x04
// Device capability type
descDeviceCapTypeWireless = 0x01
descDeviceCapTypeUSB20Extension = 0x02
descDeviceCapTypeSuperspeed = 0x03
// Device capability attributes (USB 2.0 extension)
descDeviceCapExtAttrLPMMsk = 0x02
descDeviceCapExtAttrLPMPos = 1
descDeviceCapExtAttrBESLMsk = 0x04
descDeviceCapExtAttrBESLPos = 2
// Device class
descDeviceClassCodeMisc = 0xEF
descDeviceSubClassCommon = 0x02
descDeviceProtocolIAD = 0x01
)
// descEndpointInvalid represents an invalid endpoint address.
const descEndpointInvalid = ^uint8(descEndptAddrNumberMsk | descEndptAddrDirectionMsk)
const (
descDirOut = descRequestTypeDirOut >> descRequestTypeDirPos
descDirIn = descRequestTypeDirIn >> descRequestTypeDirPos
descDirRx = descDirOut // "IN" and "OUT" terms are from host's perspective,
descDirTx = descDirIn // which is opposite from USB device. Kinda awkward.
)
// device returns the enumerated device descriptor value, defined per USB
// specification, for the receiver Speed s.
func (s Speed) device() uint32 {
switch s {
case LowSpeed:
return descDeviceSpeedLow
case FullSpeed:
return descDeviceSpeedFull
case HighSpeed:
return descDeviceSpeedHigh
case SuperSpeed, DualSuperSpeed:
return descDeviceSpeedSuper
default: // unrecognized Speed defaults to full-speed
return descDeviceSpeedFull
}
}
const (
// Common attributes for all endpoint descriptor configurations.
descEndptConfigAttr = descConfigAttrD7Msk | // Bit 7: reserved (1)
(0 << descConfigAttrSelfPoweredPos) | // Bit 6: self-powered
(0 << descConfigAttrRemoteWakeupPos) | // Bit 5: remote wakeup
0 // Bits 0-4: reserved (0)
descEndptConfigAttrRxPos = 0
descEndptConfigAttrTxPos = 16
descEndptConfigAttrRxMsk = (descEndptAttrSyncTypeMsk | descEndptConfigAttr) << descEndptConfigAttrRxPos
descEndptConfigAttrTxMsk = (descEndptAttrSyncTypeMsk | descEndptConfigAttr) << descEndptConfigAttrTxPos
descEndptConfigAttrRxUnused = 0x02 << descEndptConfigAttrRxPos
descEndptConfigAttrTxUnused = 0x02 << descEndptConfigAttrTxPos
descEndptConfigAttrRxIsochronous = (descEndptAttrSyncTypeAsync | descEndptConfigAttr) << descEndptConfigAttrRxPos
descEndptConfigAttrTxIsochronous = (descEndptAttrSyncTypeAsync | descEndptConfigAttr) << descEndptConfigAttrTxPos
descEndptConfigAttrRxBulk = (descEndptAttrSyncTypeAdaptive | descEndptConfigAttr) << descEndptConfigAttrRxPos
descEndptConfigAttrTxBulk = (descEndptAttrSyncTypeAdaptive | descEndptConfigAttr) << descEndptConfigAttrTxPos
descEndptConfigAttrRxInterrupt = (descEndptAttrSyncTypeSync | descEndptConfigAttr) << descEndptConfigAttrRxPos
descEndptConfigAttrTxInterrupt = (descEndptAttrSyncTypeSync | descEndptConfigAttr) << descEndptConfigAttrTxPos
)
type (
// descString is the actual byte array used to hold string descriptors. The
// first two bytes are a USB-specified header (0=length, 1=type), and the
// remaining bytes are UTF-16 code points, ordered low byte-first. If you just
// want to use UTF-8 (or even ASCII), you still need to reserve 2 bytes for
// each symbol, but you can set all of their high bytes 0.
descString [descStringSize]uint8
// descStringIndex defines an indexed collection of string descriptors for a
// given language.
descStringIndex [descStringIndexCount]descString
// descStringLanguage contains a language code and an indexed collection of
// string descriptors encoded in that language.
descStringLanguage struct {
language uint16
descriptor descStringIndex
}
)
const (
descStringIndexCount = 4 // Language, Manufacturer, Product, Serial Number
descStringSize = 64 // (64-2)/2 = 31 chars each (UTF-16 code points)
// The maximum allowable string descriptor size is 255, or (255-2)/2 = 126
// available UTF-16 code points. Considering we are allocating this storage at
// compile-time, it seems like an awful waste of space (255*4 = ~1 KiB) just
// to store four strings, which, in all likelihood, will not be modified by
// anyone other than TinyGo devs; 64*4 = 256 B (i.e., 31 UTF-16 code points
// for each string) seems a good compromise.
)
-34
View File
@@ -1,34 +0,0 @@
//go:build atsamd51 || atsame5x
// +build atsamd51 atsame5x
package usb
// descCPUFrequencyHz defines the target CPU frequency (Hz).
const descCPUFrequencyHz = 120000000
// descCoreCount defines the number of USB PHY cores available on this platform,
// independent of the number of cores which shall be configured as TinyGo USB
// host/device controller instances.
const descCoreCount = 1 // SAMx51 has a single, full-speed USB PHY
// General USB device identification constants.
const (
descCommonVendorID = 0x03EB
descCommonProductID = 0x2421
descCommonReleaseID = 0x0101 // BCD (1.1)
descCommonLanguage = descLanguageEnglish
descCommonManufacturer = "TinyGo"
descCommonProduct = "USB"
descCommonSerialNumber = "00000"
)
// Constants for all USB device classes.
const (
// USB endpoints parameters
descMaxEndpoints = 8 // SAMx51 maximum number of endpoints
descControlPacketSize = 64
)
-622
View File
@@ -1,622 +0,0 @@
//go:build mimxrt1062
// +build mimxrt1062
package usb
// descCPUFrequencyHz defines the target CPU frequency (Hz).
const descCPUFrequencyHz = 600000000
// descCoreCount defines the number of USB PHY cores available on this platform,
// independent of the number of cores which shall be configured as TinyGo USB
// host/device controller instances.
const descCoreCount = 2
// descCDCACMCount defines the number of USB cores that may be configured as
// CDC-ACM (single) devices.
const descCDCACMCount = 1
// descHIDCount defines the number of USB cores that may be configured as a
// composite (keyboard + mouse + joystick) human interface device (HID).
const descHIDCount = 0
// General USB device identification constants.
const (
descCommonVendorID = 0x16C0
descCommonProductID = 0x0483
descCommonReleaseID = 0x0101 // BCD (1.1)
descCommonLanguage = descLanguageEnglish
descCommonManufacturer = "TinyGo"
descCommonProduct = "USB"
descCommonSerialNumber = "00000"
)
// Constants for USB CDC-ACM device classes.
const (
// USB bus configuration attributes
descCDCACMMaxPowerMa = 100 // Maximum current (mA) requested from host
// CDC-ACM Control Buffers
descCDCACMQHCount = 2 * (descCDCACMEndpointCount + 1)
descCDCACMCxCount = 8
// CDC-ACM Data Buffers
descCDCACMRDCount = 2 * descCDCACMEndpointCount
descCDCACMRxSize = descCDCACMDataRxPacketSize
descCDCACMRxCount = descCDCACMRxSize * descCDCACMRDCount
descCDCACMTDCount = descCDCACMEndpointCount
descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize
descCDCACMTxCount = descCDCACMTxSize * descCDCACMTDCount
descCDCACMTxTimeoutMs = 120 // millisec
descCDCACMTxSyncUs = 75 // microsec
// Default CDC-ACM Endpoint Configurations (High-Speed)
descCDCACMStatusInterval = descCDCACMStatusHSInterval // Status
descCDCACMStatusPacketSize = descCDCACMStatusHSPacketSize //
descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // Data Rx
descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // Data Tx
// CDC-ACM Endpoint Configurations for Full-Speed Device
descCDCACMStatusFSInterval = 5 // Status
descCDCACMStatusFSPacketSize = 16 // (full-speed)
descCDCACMDataRxFSPacketSize = 64 // Data Rx (full-speed)
descCDCACMDataTxFSPacketSize = 64 // Data Tx (full-speed)
// CDC-ACM Endpoint Configurations for High-Speed Device
descCDCACMStatusHSInterval = 5 // Status
descCDCACMStatusHSPacketSize = 16 // (high-speed)
descCDCACMDataRxHSPacketSize = 512 // Data Rx (high-speed)
descCDCACMDataTxHSPacketSize = 512 // Data Tx (high-speed)
)
// Constants for USB HID (keyboard, mouse, joystick) device classes.
const (
// USB bus configuration attributes
descHIDMaxPowerMa = 100 // Maximum current (mA) requested from host
// HID Control Buffers
descHIDQHCount = 2 * (descHIDEndpointCount + 1)
descHIDCxCount = 8
// HID Serial Buffers
descHIDSerialRDCount = 8
descHIDSerialRxSize = descHIDSerialRxPacketSize
descHIDSerialRxCount = descHIDSerialRxSize * descHIDSerialRDCount
descHIDSerialTDCount = 12
descHIDSerialTxSize = descHIDSerialTxPacketSize
descHIDSerialTxCount = descHIDSerialTxSize * descHIDSerialTDCount
descHIDSerialTxTimeoutMs = 50 // millisec
descHIDSerialTxSyncUs = 75 // microsec
// HID Keyboard Buffers
descHIDKeyboardTDCount = 12
descHIDKeyboardTxSize = 4 * descHIDKeyboardTxPacketSize
descHIDKeyboardTxCount = descHIDKeyboardTxSize * descHIDKeyboardTDCount
descHIDKeyboardTxTimeoutMs = 50 // millisec
// HID Mouse Buffers
descHIDMouseTDCount = 4
descHIDMouseTxSize = 4 * descHIDMouseTxPacketSize
descHIDMouseTxCount = descHIDMouseTxSize * descHIDMouseTDCount
descHIDMouseTxTimeoutMs = 30 // millisec
// HID Joystick Buffers
descHIDJoystickTDCount = 4
descHIDJoystickTxSize = 4 * descHIDJoystickTxPacketSize
descHIDJoystickTxCount = descHIDJoystickTxSize * descHIDJoystickTDCount
descHIDJoystickTxTimeoutMs = 30 // millisec
// Default HID Endpoint Configurations (High-Speed)
descHIDSerialRxInterval = descHIDSerialRxHSInterval // Serial Rx
descHIDSerialRxPacketSize = descHIDSerialRxHSPacketSize //
descHIDSerialTxInterval = descHIDSerialTxHSInterval // Serial Tx
descHIDSerialTxPacketSize = descHIDSerialTxHSPacketSize //
descHIDKeyboardTxInterval = descHIDKeyboardTxHSInterval // Keyboard
descHIDKeyboardTxPacketSize = descHIDKeyboardTxHSPacketSize //
descHIDMediaKeyTxInterval = descHIDMediaKeyTxHSInterval // Keyboard Media Keys
descHIDMediaKeyTxPacketSize = descHIDMediaKeyTxHSPacketSize //
descHIDMouseTxInterval = descHIDMouseTxHSInterval // Mouse
descHIDMouseTxPacketSize = descHIDMouseTxHSPacketSize //
descHIDJoystickTxInterval = descHIDJoystickTxHSInterval // Joystick
descHIDJoystickTxPacketSize = descHIDJoystickTxHSPacketSize //
// HID Endpoint Configurations for Full-Speed Device
descHIDSerialRxFSInterval = 2 // Serial Rx
descHIDSerialRxFSPacketSize = 8 // (full-speed)
descHIDSerialTxFSInterval = 1 // Serial Tx
descHIDSerialTxFSPacketSize = 16 // (full-speed)
descHIDKeyboardTxFSInterval = 4 // Keyboard
descHIDKeyboardTxFSPacketSize = 8 // (full-speed)
descHIDMediaKeyTxFSInterval = 4 // Keyboard Media Keys
descHIDMediaKeyTxFSPacketSize = 8 // (full-speed)
descHIDMouseTxFSInterval = 4 // Mouse
descHIDMouseTxFSPacketSize = 8 // (full-speed)
descHIDJoystickTxFSInterval = 4 // Joystick
descHIDJoystickTxFSPacketSize = 12 // (full-speed)
// HID Endpoint Configurations for High-Speed Device
descHIDSerialRxHSInterval = 2 // Serial
descHIDSerialRxHSPacketSize = 32 // (high-speed)
descHIDSerialTxHSInterval = 1 // Serial Tx
descHIDSerialTxHSPacketSize = 64 // (high-speed)
descHIDKeyboardTxHSInterval = 1 // Keyboard
descHIDKeyboardTxHSPacketSize = 8 // (high-speed)
descHIDMediaKeyTxHSInterval = 4 // Keyboard Media Keys
descHIDMediaKeyTxHSPacketSize = 8 // (high-speed)
descHIDMouseTxHSInterval = 1 // Mouse
descHIDMouseTxHSPacketSize = 8 // (high-speed)
descHIDJoystickTxHSInterval = 2 // Joystick
descHIDJoystickTxHSPacketSize = 12 // (high-speed)
)
// descCDCACM0QH is an array of endpoint queue heads, which is where all
// transfers for a given endpoint are managed, for the default CDC-ACM (single)
// device class configuration (index 1).
//
// From the iMXRT1062 Reference Manual:
//
// Software must ensure that no interface data structure reachable
// by the Device Controller spans a 4K-page boundary.
//
// The [queue head] is a 48-byte data structure, but must be aligned on
// 64-byte boundaries.
//
// Endpoint queue heads are arranged in an array in a continuous area of
// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered
// device queue heads in the list support receive endpoints (OUT/SETUP) and
// the odd-numbered queue heads in the list are used for transmit endpoints
// (IN/INTERRUPT). The device controller will index into this array based upon
// the endpoint number received from the USB bus. All information necessary to
// respond to transactions for all primed transfers is contained in this list
// so the Device Controller can readily respond to incoming requests without
// having to traverse a linked list.
//go:align 4096
var descCDCACM0QH [descCDCACMQHCount]dhwEndpoint
// descCDCACM0CD is the transfer descriptor for messages transmitted or received
// on the status/control endpoint 0 for the default CDC-ACM (single) device
// class configuration (index 1).
//go:align 32
var descCDCACM0CD dhwTransfer
// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of
// the default CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDCACM0Cx [descCDCACMCxCount]uint8
// descCDCACM0AD is the transfer descriptor for ackowledgement (ACK) messages
// transmitted or received on the status/control endpoint 0 for the default
// CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDCACM0AD dhwTransfer
// descCDCACM0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0
// for the default CDC-ACM (single) device class configuration (index 1).
//go:align 32
var descCDCACM0Dx [descCDCACMConfigSize]uint8
// descCDCACM0RD is an array of transfer descriptors for Rx (OUT) transfers,
// which describe to the device controller the location and quantity of data
// being received for a given transfer, for the default CDC-ACM (single) device
// class configuration (index 1).
//go:align 32
var descCDCACM0RD [descCDCACMRDCount]dhwTransfer
// descCDCACM0Rx is the receive (Rx) transfer buffer for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDCACM0Rx [descCDCACMRxCount]uint8
// descCDCACM0TD is an array of transfer descriptors for Tx (IN) transfers,
// which describe to the device controller the location and quantity of data
// being transmitted for a given transfer, for the default CDC-ACM (single)
// device class configuration (index 1).
//go:align 32
var descCDCACM0TD [descCDCACMTDCount]dhwTransfer
// descCDCACM0Tx is the transmit (Tx) transfer buffer for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDCACM0Tx [descCDCACMTxCount]uint8
var descCDCACM0RDNum [descCDCACMRDCount]uint16
var descCDCACM0RDIdx [descCDCACMRDCount]uint16
var descCDCACM0RDQue [(descCDCACMRDCount + 1)]uint16
// descCDCACMClassData holds the buffers and control states for all CDC-ACM
// (single) device class configurations, ordered by index (offset by -1), for
// iMXRT1062 targets only.
//
// Instances of this type (elements of descCDCACMData) are embedded in elements
// of the common/target-agnostic CDC-ACM class configurations (descCDCACM).
// Methods defined on this type implement target-specific functionality, and
// some of these methods are required by the common device controller driver.
// Thus, this type functions as a hardware abstraction layer (HAL).
type descCDCACMClassData struct {
// CDC-ACM Control Buffers
qh *[descCDCACMQHCount]dhwEndpoint // endpoint queue heads
cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor
cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
dx *[descCDCACMConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
// CDC-ACM Data Buffers
rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors
rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer
td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors
tx *[descCDCACMTxCount]uint8 // bulk data endpoint Tx (IN) transfer buffer
rxCount *[descCDCACMRDCount]uint16
rxIndex *[descCDCACMRDCount]uint16
rxQueue *[(descCDCACMRDCount + 1)]uint16
sxSize uint16
rxSize uint16
txSize uint16
txHead uint8
txFree uint16
txPrev bool
rxHead uint8
rxTail uint8
rxFree uint16
}
// descCDCACMData holds statically-allocated instances for each of the target-
// specific (iMXRT1062) CDC-ACM (single) device class configurations' control
// and data structures, ordered by configuration index (offset by -1). Each
// element is embedded in a corresponding element of descCDCACM.
//go:align 64
var descCDCACMData = [dcdCount]descCDCACMClassData{
{ // -- CDC-ACM (single) Class Configuration Index 1 --
// CDC-ACM Control Buffers
qh: &descCDCACM0QH,
cd: &descCDCACM0CD,
cx: &descCDCACM0Cx,
ad: &descCDCACM0AD,
dx: &descCDCACM0Dx,
// CDC-ACM Data Buffers
rd: &descCDCACM0RD,
rx: &descCDCACM0Rx,
td: &descCDCACM0TD,
tx: &descCDCACM0Tx,
rxCount: &descCDCACM0RDNum,
rxIndex: &descCDCACM0RDIdx,
rxQueue: &descCDCACM0RDQue,
sxSize: descCDCACMStatusPacketSize,
rxSize: descCDCACMDataRxPacketSize,
txSize: descCDCACMDataTxPacketSize,
},
}
// descHID0QH is an array of endpoint queue heads, which is where all transfers
// for a given endpoint are managed, for the default HID device class
// configuration (index 1).
//
// From the iMXRT1062 Reference Manual:
//
// Software must ensure that no interface data structure reachable
// by the Device Controller spans a 4K-page boundary.
//
// The [queue head] is a 48-byte data structure, but must be aligned on
// 64-byte boundaries.
//
// Endpoint queue heads are arranged in an array in a continuous area of
// memory pointed to by the USB.ENDPOINTLISTADDR pointer. The even-numbered
// device queue heads in the list support receive endpoints (OUT/SETUP) and
// the odd-numbered queue heads in the list are used for transmit endpoints
// (IN/INTERRUPT). The device controller will index into this array based upon
// the endpoint number received from the USB bus. All information necessary to
// respond to transactions for all primed transfers is contained in this list
// so the Device Controller can readily respond to incoming requests without
// having to traverse a linked list.
//go:align 4096
var descHID0QH [descHIDQHCount]dhwEndpoint
// descHID0CD is the transfer descriptor for messages transmitted or received on
// the status/control endpoint 0 for the default HID device class configuration
// (index 1).
//go:align 32
var descHID0CD dhwTransfer
// descHID0Cx is the buffer for control/status data received on endpoint 0 of
// the default HID device class configuration (index 1).
//go:align 32
var descHID0Cx [descHIDCxCount]uint8
// descHID0AD is the transfer descriptor for ackowledgement (ACK) messages
// transmitted or received on the status/control endpoint 0 for the default HID
// device class configuration (index 1).
//go:align 32
var descHID0AD dhwTransfer
// descHID0Dx is the transmit (Tx) buffer of descriptor data on endpoint 0 for
// the default HID device class configuration (index 1).
//go:align 32
var descHID0Dx [descHIDConfigSize]uint8
// descHID0SerialRD is an array of transfer descriptors for serial Rx (OUT)
// transfers, which describe to the device controller the location and quantity
// of data being received for a given transfer, for the default HID device class
// configuration (index 1).
//go:align 32
var descHID0SerialRD [descHIDSerialRDCount]dhwTransfer
// descHID0SerialRx is the serial receive (Rx) transfer buffer for the default
// HID device class configuration (index 1).
//go:align 32
var descHID0SerialRx [descHIDSerialRxCount]uint8
// descHID0SerialTD is an array of transfer descriptors for serial Tx (IN)
// transfers, which describe to the device controller the location and quantity
// of data being transmitted for a given transfer, for the default HID device
// class configuration (index 1).
//go:align 32
var descHID0SerialTD [descHIDSerialTDCount]dhwTransfer
// descHID0SerialTx is the serial transmit (Tx) transfer buffer for the default
// HID device class configuration (index 1).
//go:align 32
var descHID0SerialTx [descHIDSerialTxCount]uint8
var descHID0SerialRDIdx [descHIDSerialRDCount]uint16
var descHID0SerialRDQue [(descHIDSerialRDCount + 1)]uint16
// descHID0KeyboardTD is an array of transfer descriptors for keyboard Tx (IN)
// transfers, which describe to the device controller the location and quantity
// of data being transmitted for a given transfer, for the default HID device
// class configuration (index 1).
//go:align 32
var descHID0KeyboardTD [descHIDKeyboardTDCount]dhwTransfer
// descHID0KeyboardTx is the keyboard transmit (Tx) transfer buffer for the
// default HID device class configuration (index 1).
//go:align 32
var descHID0KeyboardTx [descHIDKeyboardTxCount]uint8
// descHID0KeyboardTp is the keyboard HID report transmit (Tx) transfer buffer
// for the default HID device class configuration (index 1).
//go:align 32
var descHID0KeyboardTp [descHIDKeyboardTxPacketSize]uint8
//go:align 32
var descHID0KeyboardTxKey [hidKeyboardKeyCount]uint8
//go:align 32
var descHID0KeyboardTxCon [hidKeyboardConCount]uint16
//go:align 32
var descHID0KeyboardTxSys [hidKeyboardSysCount]uint8
// descHID0MouseTD is an array of transfer descriptors for mouse Tx (IN)
// transfers, which describe to the device controller the location and quantity
// of data being transmitted for a given transfer, for the default HID device
// class configuration (index 1).
//go:align 32
var descHID0MouseTD [descHIDMouseTDCount]dhwTransfer
// descHID0MouseTx is the mouse transmit (Tx) transfer buffer for the default
// HID device class configuration (index 1).
//go:align 32
var descHID0MouseTx [descHIDMouseTxCount]uint8
// descHID0JoystickTD is an array of transfer descriptors for joystick Tx (IN)
// transfers, which describe to the device controller the location and quantity
// of data being transmitted for a given transfer, for the default HID device
// class configuration (index 1).
//go:align 32
var descHID0JoystickTD [descHIDJoystickTDCount]dhwTransfer
// descHID0JoystickTx is the joystick transmit (Tx) transfer buffer for the
// default HID device class configuration (index 1).
//go:align 32
var descHID0JoystickTx [descHIDJoystickTxCount]uint8
// descHID0Keyboard is the Keyboard instance with which the user may interact
// when using the default HID device class configuration (index 1).
//go:align 64
var descHID0Keyboard = Keyboard{
key: &descHID0KeyboardTxKey,
con: &descHID0KeyboardTxCon,
sys: &descHID0KeyboardTxSys,
}
// descHIDClassData holds the buffers and control states for all of the HID
// device class configurations, ordered by index (offset by -1), for iMXRT1062
// targets only.
//
// Instances of this type (elements of descHIDData) are embedded in elements
// of the common/target-agnostic HID class configurations (descHID).
// Methods defined on this type implement target-specific functionality, and
// some of these methods are required by the common device controller driver.
// Thus, this type functions as a hardware abstraction layer (HAL).
type descHIDClassData struct {
// HID Control Buffers
qh *[descHIDQHCount]dhwEndpoint // endpoint queue heads
cd *dhwTransfer // control endpoint 0 Rx/Tx transfer descriptor
cx *[descHIDCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer
// HID Serial Buffers
rdSerial *[descHIDSerialRDCount]dhwTransfer // interrupt endpoint serial Rx (OUT) transfer descriptors
rxSerial *[descHIDSerialRxCount]uint8 // interrupt endpoint serial Rx (OUT) transfer buffer
tdSerial *[descHIDSerialTDCount]dhwTransfer // interrupt endpoint serial Tx (IN) transfer descriptors
txSerial *[descHIDSerialTxCount]uint8 // interrupt endpoint serial Tx (IN) transfer buffer
rxSerialIndex *[descHIDSerialRDCount]uint16
rxSerialQueue *[(descHIDSerialRDCount + 1)]uint16
rxSerialSize uint16
txSerialSize uint16
txSerialHead uint8
txSerialFree uint16
txSerialPrev bool
rxSerialHead uint8
rxSerialTail uint8
rxSerialFree uint16
// HID Keyboard Buffers
tdKeyboard *[descHIDKeyboardTDCount]dhwTransfer // interrupt endpoint keyboard Tx (IN) transfer descriptors
txKeyboard *[descHIDKeyboardTxCount]uint8 // interrupt endpoint keyboard Tx (IN) transfer buffer
tpKeyboard *[descHIDKeyboardTxPacketSize]uint8 // interrupt endpoint keyboard Tx (IN) HID report bbuffer
txKeyboardSize uint16
txKeyboardHead uint8
txKeyboardPrev bool
// HID Mouse Buffers
tdMouse *[descHIDMouseTDCount]dhwTransfer // interrupt endpoint mouse Tx (IN) transfer descriptors
txMouse *[descHIDMouseTxCount]uint8 // interrupt endpoint mouse Tx (IN) transfer buffer
txMouseSize uint16
txMouseHead uint8
txMousePrev bool
// HID Joystick Buffers
tdJoystick *[descHIDJoystickTDCount]dhwTransfer // interrupt endpoint joystick Tx (IN) transfer descriptors
txJoystick *[descHIDJoystickTxCount]uint8 // interrupt endpoint joystick Tx (IN) transfer buffer
txJoystickSize uint16
txJoystickHead uint8
txJoystickPrev bool
// HID Device Instances
//serial *Serial
keyboard *Keyboard
//mouse *Mouse
//joystick *Joystick
}
// descHIDData holds statically-allocated instances for each of the target-
// specific (iMXRT1062) HID device class configurations' control and data
// structures, ordered by configuration index (offset by -1). Each element is
// embedded in a corresponding element of descHID.
//go:align 64
var descHIDData = [dcdCount]descHIDClassData{
{ // -- HID Class Configuration Index 1 --
// HID Control Buffers
qh: &descHID0QH,
cd: &descHID0CD,
cx: &descHID0Cx,
ad: &descHID0AD,
dx: &descHID0Dx,
// HID Serial Buffers
rdSerial: &descHID0SerialRD,
rxSerial: &descHID0SerialRx,
tdSerial: &descHID0SerialTD,
txSerial: &descHID0SerialTx,
rxSerialIndex: &descHID0SerialRDIdx,
rxSerialQueue: &descHID0SerialRDQue,
rxSerialSize: descHIDSerialRxPacketSize,
txSerialSize: descHIDSerialTxPacketSize,
// HID Keyboard Buffers
tdKeyboard: &descHID0KeyboardTD,
txKeyboard: &descHID0KeyboardTx,
tpKeyboard: &descHID0KeyboardTp,
txKeyboardSize: descHIDKeyboardTxPacketSize,
// HID Mouse Buffers
tdMouse: &descHID0MouseTD,
txMouse: &descHID0MouseTx,
txMouseSize: descHIDMouseTxPacketSize,
// HID Joystick Buffers
tdJoystick: &descHID0JoystickTD,
txJoystick: &descHID0JoystickTx,
txJoystickSize: descHIDJoystickTxPacketSize,
// HID Device Instances
//serial: &descHID0Serial,
keyboard: &descHID0Keyboard,
//mouse: &descHID0Mouse,
//joystick: &descHID0Joystick,
},
}
-243
View File
@@ -1,243 +0,0 @@
//go:build usb.cdc && (atsamd51 || atsame5x)
// +build usb.cdc
// +build atsamd51 atsame5x
package usb
import "unsafe"
//go:inline
func (d *dhw) descriptorTable() uintptr {
return uintptr(unsafe.Pointer(&descCDC[d.cc.config-1].ed[0]))
}
// endpointDescriptor returns the endpoint descriptor for the given endpoint
// address, encoded as direction D and endpoint number N with the 8-bit mask
// D000NNNN.
//go:inline
func (d *dhw) endpointDescriptor(endpoint uint8) *dhwEPDesc {
num, dir := unpackEndpoint(endpoint)
return &descCDC[d.cc.config-1].ed[num][dir]
}
//go:inline
func (d *dhw) controlSetupBuffer() uintptr {
return uintptr(unsafe.Pointer(&descCDC[d.cc.config-1].sx[0]))
}
//go:inline
func (d *dhw) controlStatusBuffer(data []uint8) uintptr {
// reference to class configuration data
c := descCDC[d.cc.config-1]
for i := range c.cx {
c.cx[i] = 0 // zero out the control reply buffer
}
// copy the given data into control reply buffer
copy(c.cx[:], data)
return uintptr(unsafe.Pointer(&c.cx[0]))
}
// =============================================================================
// [CDC-ACM] Serial UART (Virtual COM Port)
// =============================================================================
func (d *dhw) cdcConfigure() {
acm := &descCDC[d.cc.config-1]
acm.setState(descCDCStateConfigured)
// SAMx51 only supports USB full-speed (FS) operation
acm.sxSize = descCDCStatusFSPacketSize
acm.rxSize = descCDCDataRxFSPacketSize
acm.txSize = descCDCDataTxFSPacketSize
rq := acm.rxq[:]
tq := acm.txq[:]
// Rx gives priority to incoming data, Tx gives priority to outgoing data
acm.rq.Init(&rq, int(acm.rxSize), QueueFullDiscardFirst)
acm.tq.Init(&tq, int(acm.txSize), QueueFullDiscardLast)
d.endpointEnable(txEndpoint(descCDCEndpointStatus),
false, descCDCConfigAttrStatus)
d.endpointEnable(rxEndpoint(descCDCEndpointDataRx),
false, descCDCConfigAttrDataRx)
d.endpointEnable(txEndpoint(descCDCEndpointDataTx),
false, descCDCConfigAttrDataTx)
d.endpointConfigure(txEndpoint(descCDCEndpointStatus),
nil)
d.endpointConfigure(rxEndpoint(descCDCEndpointDataRx),
d.cdcReceiveComplete)
d.endpointConfigure(txEndpoint(descCDCEndpointDataTx),
d.cdcTransmitComplete)
d.cdcReceiveStart(rxEndpoint(descCDCEndpointDataRx))
}
func (d *dhw) cdcSetLineState(state uint16) {
acm := &descCDC[d.cc.config-1]
acm.setState(descCDCStateLineState)
if acm.ls.parse(state) {
// TBD: respond to changes in line state?
}
}
func (d *dhw) cdcSetLineCoding(coding []uint8) {
acm := &descCDC[d.cc.config-1]
acm.setState(descCDCStateLineCoding)
if acm.lc.parse(coding) {
switch acm.lc.baud {
case 1200:
if acm.ls.dataTerminalReady {
// reboot CPU
}
}
}
}
func (d *dhw) cdcReady() bool {
acm := &descCDC[d.cc.config-1]
// Ensure we have received SET_CONFIGURATION class request, and then both
// SET_LINE_STATE and SET_LINE_CODING CDC requests (in that order).
return d.state() == dcdStateConfigured &&
acm.st.Get() == uint8(descCDCStateLineCoding)
}
func (d *dhw) cdcReceiveStart(endpoint uint8) {
acm := &descCDC[d.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
ready, _ := d.ep[num][descDirRx].scheduleTransfer(
uintptr(unsafe.Pointer(&acm.rx[0])), acm.rxSize)
if ready {
if xfer, ok := d.ep[num][descDirRx].pendingTransfer(); ok {
// Update the active transfer descriptor on the corresponding endpoint.
d.ep[num][descDirRx].setActiveTransfer(xfer)
d.endpointTransfer(endpoint, xfer.data, xfer.size)
}
}
}
func (d *dhw) cdcReceiveComplete(endpoint uint8, size uint32) {
acm := &descCDC[d.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
if xfer, ok := d.ep[num][descDirRx].activeTransfer(); ok {
for ptr := xfer.data; ptr < xfer.data+uintptr(size); ptr++ {
acm.rq.Enq(*(*uint8)(unsafe.Pointer(ptr)))
}
}
d.ep[num][descDirRx].setActiveTransfer(nil)
d.cdcReceiveStart(endpoint)
}
func (d *dhw) cdcTransmitStart(endpoint uint8) {
acm := &descCDC[d.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
// BULK data endpoints can simply use a single time slot in the schedule, and
// repeatedly transfer from the same transmit buffer (acm.tx) as soon as the
// transaction complete callback has been called for a prior transaction.
// Do not schedule another transfer if one is already active, or if our Tx
// FIFO is currently empty.
if d.ep[num][descDirTx].hasActiveTransfer() || acm.tq.Len() == 0 {
return
}
if send, err := acm.tq.Read(acm.tx[:]); err == nil && send > 0 {
ready, _ := d.ep[num][descDirTx].scheduleTransfer(
uintptr(unsafe.Pointer(&acm.tx[0])), uint32(send))
if ready {
if xfer, ok := d.ep[num][descDirTx].pendingTransfer(); ok {
d.ep[num][descDirTx].setActiveTransfer(xfer)
d.endpointTransfer(endpoint, xfer.data, xfer.size)
}
}
}
}
func (d *dhw) cdcTransmitComplete(endpoint uint8, size uint32) {
acm := &descCDC[d.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
if size > 0 && size%acm.txSize == 0 {
// Send ZLP if transfer length is a non-zero multiple of max packet size.
d.endpointTransfer(endpoint, 0, 0)
}
d.ep[num][descDirTx].setActiveTransfer(nil)
d.cdcTransmitStart(endpoint)
}
// cdcFlush discards all buffered input (Rx) data.
func (d *dhw) cdcFlush() {
acm := &descCDC[d.cc.config-1]
acm.rq.Reset(int(acm.rxSize))
}
func (d *dhw) cdcAvailable() int {
acm := &descCDC[d.cc.config-1]
return acm.rq.Len()
}
func (d *dhw) cdcPeek() (uint8, bool) {
acm := &descCDC[d.cc.config-1]
return acm.rq.Front()
}
func (d *dhw) cdcReadByte() (uint8, bool) {
acm := &descCDC[d.cc.config-1]
return acm.rq.Deq()
}
func (d *dhw) cdcRead(data []uint8) (int, error) {
acm := &descCDC[d.cc.config-1]
return acm.rq.Read(data)
}
func (d *dhw) cdcWriteByte(c uint8) error {
_, err := d.cdcWrite([]uint8{c})
return err
}
func (d *dhw) cdcWrite(data []uint8) (int, error) {
acm := &descCDC[d.cc.config-1]
num := uint16(descCDCEndpointDataTx) & descEndptAddrNumberMsk
var sent int
var werr error
for off := 0; off < len(data); off += int(acm.txSize) {
cnt := len(data[off:])
if cnt > int(acm.txSize) {
cnt = int(acm.txSize)
}
// Block until we have room in the Tx FIFO. Space will become available once
// the endpoint transaction complete interrupt is raised for the Tx BULK data
// endpoint, and then the uartTransmitComplete callback has dequeued data from
// the Tx FIFO (acm.tq) into the Tx transmit buffer (acm.tx).
for acm.tq.Rem() < cnt {
}
// Add data to Tx FIFO
add, err := acm.tq.Write(data[off : off+cnt])
if err != nil {
werr = err
break
}
sent += add
if d.ep[num][descDirTx].hasActiveTransfer() {
// If there is already a transmit in-progress, wait for its callback to
// detect new data in the FIFO and continue the transfer automatically.
} else {
// Otherwise, initiate a new data transfer.
d.cdcTransmitStart(txEndpoint(descCDCEndpointDataTx))
}
}
return sent, werr
}
-265
View File
@@ -1,265 +0,0 @@
//go:build usb.hid && (atsamd51 || atsame5x)
// +build usb.hid
// +build atsamd51 atsame5x
package usb
import "unsafe"
//go:inline
func (d *dhw) descriptorTable() uintptr {
return uintptr(unsafe.Pointer(&descHID[d.cc.config-1].ed[0]))
}
// endpointDescriptor returns the endpoint descriptor for the given endpoint
// address, encoded as direction D and endpoint number N with the 8-bit mask
// D000NNNN.
//go:inline
func (d *dhw) endpointDescriptor(endpoint uint8) *dhwEPDesc {
num, dir := unpackEndpoint(endpoint)
return &descHID[d.cc.config-1].ed[num][dir]
}
//go:inline
func (d *dhw) controlSetupBuffer() uintptr {
return uintptr(unsafe.Pointer(&descHID[d.cc.config-1].sx[0]))
}
//go:inline
func (d *dhw) controlStatusBuffer(data []uint8) uintptr {
// reference to class configuration data
c := descHID[d.cc.config-1]
for i := range c.cx {
c.cx[i] = 0 // zero out the control reply buffer
}
// copy the given data into control reply buffer
copy(c.cx[:], data)
return uintptr(unsafe.Pointer(&c.cx[0]))
}
// =============================================================================
// [HID] Serial
// =============================================================================
func (d *dhw) serialConfigure() {
// hid := &descHID[d.cc.config-1]
// SAMx51 only supports USB full-speed (FS) operation
// hid.rxSerialSize = descHIDSerialRxFSPacketSize
// hid.txSerialSize = descHIDSerialTxFSPacketSize
// Rx and Tx are on same endpoint
d.endpointEnable(descHIDEndpointSerialRx,
false, descHIDConfigAttrSerial)
// d.endpointConfigureRx(descHIDEndpointSerialRx,
// hid.rxSerialSize, false, d.serialNotify)
// d.endpointConfigureTx(descHIDEndpointSerialTx,
// hid.txSerialSize, false, nil)
// for i := range hid.rdSerial {
// d.serialReceive(uint8(i))
// }
// d.timerConfigure(0, descHIDSerialTxSyncUs, d.serialSync)
}
func (d *dhw) serialReceive(endpoint uint8) {
hid := &descHID[d.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
_, _ = hid, num // TODO(ardnew): elaborate stub
}
func (d *dhw) serialTransmit() {
hid := &descHID[d.cc.config-1]
_ = hid // TODO(ardnew): elaborate stub
}
func (d *dhw) serialNotify( /* transfer *dhwTransfer */ ) {
// hid := &descHID[d.cc.config-1]
// len := hid.rxSerialSize - (uint16(transfer.token>>16) & 0x7FFF)
// _ = len // TODO(ardnew): elaborate stub
}
// serialFlush discards all buffered input (Rx) data.
func (d *dhw) serialFlush() {
hid := &descHID[d.cc.config-1]
_ = hid
}
func (d *dhw) serialSync() {
}
// =============================================================================
// [HID] Keyboard
// =============================================================================
func (d *dhw) keyboard() *Keyboard { return descHID[d.cc.config-1].keyboard }
func (d *dhw) keyboardConfigure() {
hid := &descHID[d.cc.config-1]
// Initialize keyboard
hid.keyboard.configure(d.dcd, hid)
// SAMx51 only supports USB full-speed (FS) operation
hid.txKeyboardSize = descHIDKeyboardTxPacketSize
// tq := hid.txqKeyboard[:]
// hid.tqKeyboard.Init(&tq, len(hid.txqKeyboard), QueueFullDiscardFirst)
d.endpointEnable(txEndpoint(descHIDEndpointKeyboard),
false, descHIDConfigAttrKeyboard)
d.endpointEnable(txEndpoint(descHIDEndpointMediaKey),
false, descHIDConfigAttrMediaKey)
d.endpointConfigure(txEndpoint(descHIDEndpointKeyboard),
d.keyboardWriteComplete)
d.endpointConfigure(txEndpoint(descHIDEndpointMediaKey),
d.keyboardWriteComplete)
}
func (d *dhw) keyboardSendKeys(consumer bool) bool {
hid := &descHID[d.cc.config-1]
data := [8]uint8{}
if !consumer {
data[0] = hid.keyboard.mod
data[1] = 0
data[2] = hid.keyboard.key[0]
data[3] = hid.keyboard.key[1]
data[4] = hid.keyboard.key[2]
data[5] = hid.keyboard.key[3]
data[6] = hid.keyboard.key[4]
data[7] = hid.keyboard.key[5]
return d.keyboardWrite(txEndpoint(descHIDEndpointKeyboard), data[:])
} else {
// 44444444 44333333 33332222 22222211 11111111 [ word ]
// 98765432 10987654 32109876 54321098 76543210 [ index ] (right-to-left)
data[1] = uint8((hid.keyboard.con[1] << 2) | ((hid.keyboard.con[0] >> 8) & 0x03))
data[2] = uint8((hid.keyboard.con[2] << 4) | ((hid.keyboard.con[1] >> 6) & 0x0F))
data[3] = uint8((hid.keyboard.con[3] << 6) | ((hid.keyboard.con[2] >> 4) & 0x3F))
data[4] = uint8(hid.keyboard.con[3] >> 2)
data[5] = hid.keyboard.sys[0]
data[6] = hid.keyboard.sys[1]
data[7] = hid.keyboard.sys[2]
return d.keyboardWrite(txEndpoint(descHIDEndpointMediaKey), data[:])
}
}
func (d *dhw) keyboardWriteComplete(endpoint uint8, size uint32) {
hid := &descHID[d.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
if size > 0 && size%uint32(hid.txKeyboardSize) == 0 {
// Send ZLP if transfer length is a non-zero multiple of max packet size.
d.endpointTransfer(endpoint, 0, 0)
}
d.ep[num][descDirTx].setActiveTransfer(nil)
}
func (d *dhw) keyboardWrite(endpoint uint8, data []uint8) bool {
hid := &descHID[d.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
for off := 0; off < len(data); off += int(hid.txKeyboardSize) {
cnt := len(data[off:])
if cnt > int(hid.txKeyboardSize) {
cnt = int(hid.txKeyboardSize)
}
for d.ep[num][descDirTx].hasActiveTransfer() {
}
ready, _ := d.ep[num][descDirTx].scheduleTransfer(
uintptr(unsafe.Pointer(&data[0])), uint32(cnt))
if ready {
if xfer, ok := d.ep[num][descDirTx].pendingTransfer(); ok {
d.ep[num][descDirTx].setActiveTransfer(xfer)
d.endpointTransfer(endpoint, xfer.data, xfer.size)
}
}
}
// size := uint16(len(data))
// xfer := &hid.tdKeyboard[hid.txKeyboardHead]
// when := ticks()
// for {
// if 0 == xfer.token&0x80 {
// if 0 != xfer.token&0x68 {
// // TODO: token contains error, how to handle?
// }
// hid.txKeyboardPrev = false
// break
// }
// if hid.txKeyboardPrev {
// return false
// }
// if ticks()-when > descHIDKeyboardTxTimeoutMs {
// // Waited too long, assume host connection dropped
// hid.txKeyboardPrev = true
// return false
// }
// }
// // Without this delay, the order packets are transmitted is seriously screwy.
// udelay(60)
// buff := hid.txKeyboard[hid.txKeyboardHead*descHIDKeyboardTxSize:]
// _ = copy(buff, data)
// d.transferPrepare(xfer, &buff[0], size, 0)
// flushCache(uintptr(unsafe.Pointer(&buff[0])), descHIDKeyboardTxSize)
// d.endpointTransmit(endpoint, xfer)
// hid.txKeyboardHead += 1
// if hid.txKeyboardHead >= descHIDKeyboardTDCount {
// hid.txKeyboardHead = 0
// }
return true
}
// =============================================================================
// [HID] Mouse
// =============================================================================
func (d *dhw) mouseConfigure() {
// hid := &descHID[d.cc.config-1]
// SAMx51 only supports USB full-speed (FS) operation
// hid.txMouseSize = descHIDMouseTxFSPacketSize
d.endpointEnable(descHIDEndpointMouse,
false, descHIDConfigAttrMouse)
// d.endpointConfigureTx(descHIDEndpointMouse,
// hid.txMouseSize, false, nil)
}
// =============================================================================
// [HID] Joystick
// =============================================================================
func (d *dhw) joystickConfigure() {
// hid := &descHID[d.cc.config-1]
// SAMx51 only supports USB full-speed (FS) operation
// hid.txJoystickSize = descHIDJoystickTxFSPacketSize
d.endpointEnable(descHIDEndpointJoystick,
false, descHIDConfigAttrJoystick)
// d.endpointConfigureTx(descHIDEndpointJoystick,
// hid.txJoystickSize, false, nil)
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-53
View File
@@ -1,53 +0,0 @@
package usb
// Implementation of target-agnostic USB host controller driver (hcd).
// hcdCount defines the number of USB cores to configure for host mode. It is
// computed as the sum of all declared host configuration descriptors.
const hcdCount = 0 // + ...
// hcdInstance provides statically-allocated instances of each USB host
// controller configured on this platform.
var hcdInstance [hcdCount]hcd
// hhwInstance provides statically-allocated instances of each USB hardware
// abstraction for ports configured as host on this platform.
var hhwInstance [hcdCount]hhw
// hcd implements a generic USB host controller driver (hcd) for all targets.
type hcd struct {
*hhw // USB hardware abstraction layer
core *core // Parent USB core this instance is attached to
port int // USB port index
cc class // USB host class
id int // USB host controller index
}
// initHCD initializes and assigns a free host controller instance to the given
// USB port. Returns the initialized host controller or nil if no free host
// controller instances remain.
func initHCD(port int, speed Speed, class class) (*hcd, status) {
if 0 == hcdCount {
return nil, statusInvalid // Must have defined host controllers
}
switch class.id {
default:
}
// Return the first instance whose assigned core is currently nil.
for i := range hcdInstance {
if nil == hcdInstance[i].core {
// Initialize host controller.
hcdInstance[i].hhw = allocHHW(port, i, speed, &hcdInstance[i])
hcdInstance[i].core = &coreInstance[port]
hcdInstance[i].port = port
hcdInstance[i].cc = class
hcdInstance[i].id = i
return &hcdInstance[i], statusOK
}
}
return nil, statusBusy // No free host controller instances available.
}
// class returns the receiver's current host class configuration.
func (h *hcd) class() class { return h.cc }
-61
View File
@@ -1,61 +0,0 @@
//go:build atsamd51 || atsame5x
// +build atsamd51 atsame5x
package usb
// Implementation of USB host controller driver (hcd) for Microchip SAMD51.
import (
"device/sam"
"runtime/interrupt"
)
// hhwInterruptPriority defines the priority for all USB host interrupts.
const hhwInterruptPriority = 3
// hhw implements USB host controller hardware abstraction interface.
type hhw struct {
*hcd // USB host controller driver
bus *sam.USB_HOST_Type // USB core registers
irq interrupt.Interrupt // USB IRQ, only a single interrupt on SAMx51
speed Speed
}
// allocHHW returns a reference to the USB hardware abstraction for the given
// host controller driver. Should be called only one time and during host
// controller initialization.
func allocHHW(port, instance int, speed Speed, hc *hcd) *hhw {
switch port {
case 0:
hhwInstance[instance].hcd = hc
hhwInstance[instance].bus = sam.USB_HOST
}
// Port defaults to full-speed (12 Mbit/sec) on SAMx51
if 0 == speed {
speed = HighSpeed
}
hhwInstance[instance].speed = speed
return &hhwInstance[instance]
}
// init configures the USB port for host mode operation by initializing all
// endpoint and transfer descriptor data structures, initializing core registers
// and interrupts, resetting the USB PHY, and enabling power on the bus.
func (h *hhw) init() status {
return statusOK
}
// enable causes the USB core to enter (or exit) the normal run state and
// enables/disables all interrupts on the receiver's USB port.
func (h *hhw) enable(enable bool) {
if enable {
h.irq.Enable() // Enable USB interrupts
} else {
h.irq.Disable() // Disable USB interrupts
}
}
-67
View File
@@ -1,67 +0,0 @@
// +build mimxrt1062
package usb
// Implementation of USB host controller driver (hcd) for NXP iMXRT1062.
import (
"device/nxp"
"runtime/interrupt"
)
// hhwInterruptPriority defines the priority for all USB host interrupts.
const hhwInterruptPriority = 3
// hhw implements USB host controller hardware abstraction interface.
type hhw struct {
*hcd // USB host controller driver
bus *nxp.USB_Type // USB core register
phy *nxp.USBPHY_Type // USB PHY register
irq interrupt.Interrupt // USB IRQ, only a single interrupt on iMXRT1062
speed Speed
}
// allocHHW returns a reference to the USB hardware abstraction for the given
// host controller driver. Should be called only one time and during host
// controller initialization.
func allocHHW(port, instance int, speed Speed, hc *hcd) *hhw {
switch port {
case 0:
hhwInstance[instance].hcd = hc
hhwInstance[instance].bus = nxp.USB1
hhwInstance[instance].phy = nxp.USBPHY1
case 1:
hhwInstance[instance].hcd = hc
hhwInstance[instance].bus = nxp.USB2
hhwInstance[instance].phy = nxp.USBPHY2
}
// Both ports default to high-speed (480 Mbit/sec) on Teensy 4.x
if 0 == speed {
speed = HighSpeed
}
hhwInstance[instance].speed = speed
return &hhwInstance[instance]
}
// init configures the USB port for host mode operation by initializing all
// endpoint and transfer descriptor data structures, initializing core registers
// and interrupts, resetting the USB PHY, and enabling power on the bust.
func (h *hhw) init() status {
return statusOK
}
// enable causes the USB core to enter (or exit) the normal run state and
// enables/disables all interrupts on the receiver's USB port.
func (h *hhw) enable(enable bool) {
if enable {
h.irq.Enable() // Enable USB interrupts
} else {
h.irq.Disable() // Disable USB interrupts
}
}
+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
}
@@ -1,437 +1,4 @@
//go:build usb.hid
// +build usb.hid
package usb
import "errors"
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")
)
// 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 {
dc *dcd
hc *descHIDClass
// 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
}
// 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
)
// configure initializes the receiver Keyboard by associating it with the given
// USB device controller driver and HID class configuration.
func (kb *Keyboard) configure(dc *dcd, hc *descHIDClass) {
kb.dc = dc
kb.hc = hc
}
func (kb *Keyboard) ready() bool {
return kb.dc != nil && kb.hc != nil
}
// 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 {
kb.mod = c.mod()
kb.key[0] = c.key()
kb.key[1] = 0
kb.key[2] = 0
kb.key[3] = 0
kb.key[4] = 0
kb.key[5] = 0
if !kb.dc.keyboardSendKeys(false) {
return ErrHIDReportTransfer
}
kb.mod = 0
kb.key[0] = 0
if !kb.dc.keyboardSendKeys(false) {
return 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)
}
// 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.dc.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.dc.keyboardSendKeys(false) {
return 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.dc.keyboardSendKeys(true) {
return 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.dc.keyboardSendKeys(true) {
return 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.dc.keyboardSendKeys(false) {
return 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.dc.keyboardSendKeys(true) {
return 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.dc.keyboardSendKeys(false) {
return 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.dc.keyboardSendKeys(true) {
return 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.dc.keyboardSendKeys(true) {
return ErrHIDReportTransfer
}
return nil
}
}
return nil
}
package keyboard
// Keycode is a package-defined bitmap used to encode the value of a given key.
type Keycode uint16
+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)
}
}
-293
View File
@@ -1,293 +0,0 @@
package usb
import (
"errors"
"runtime/volatile"
)
type QueueFullDiscardMode uint8
const (
QueueFullDiscardLast QueueFullDiscardMode = iota // Drop incoming data
QueueFullDiscardFirst // Drop outgoing data
)
type Queue struct {
mode QueueFullDiscardMode
size volatile.Register32
fifo *[]uint8
tail volatile.Register32 // New elements are enqueued at index tail
head volatile.Register32 // Oldest element in queue is at index head
}
var (
ErrQueueReadZero = errors.New("copy into zero-length buffer")
ErrQueueWriteZero = errors.New("copy from zero-length buffer")
ErrQueueEmpty = errors.New("buffer empty") // Read underrun
ErrQueueFull = errors.New("buffer full") // Write overrun
ErrQueueDiscardMode = errors.New("unknown discard mode")
)
// Init initializes the receiver queue's backing data store with the given byte
// slice fifo and logical capacity size. If size is greater than the slice's
// physical length, uses the slice's physical length.
func (q *Queue) Init(fifo *[]uint8, size int, mode QueueFullDiscardMode) {
q.mode = mode
q.fifo = fifo
q.Reset(size)
}
// Reset discards all buffered data and sets the FIFO logical capacity.
// If size is less than 0 or greater than FIFO physical length, uses FIFO
// physical length.
//go:inline
func (q *Queue) Reset(size int) {
if phy := len(*q.fifo); size < 0 || size > phy {
size = phy
}
q.size.Set(uint32(size))
q.tail.Set(0)
q.head.Set(0)
}
// Cap returns the logical capacity of the receiver FIFO.
//go:inline
func (q *Queue) Cap() int {
return int(q.size.Get())
}
// Len returns the number of elements enqueued in the receiver FIFO.
//go:inline
func (q *Queue) Len() int {
return int(q.tail.Get() - q.head.Get())
}
// Rem returns the number of elements not enqueued in the receiver FIFO.
//go:inline
func (q *Queue) Rem() int {
return q.Cap() - q.Len()
}
// Deq dequeues and returns the element at the front of the receiver FIFO and true.
// If the FIFO is empty and no element was dequeued, returns 0 and false.
func (q *Queue) Deq() (uint8, bool) {
head := q.head.Get()
if head == q.tail.Get() {
return 0, false
} // empty queue
data := (*q.fifo)[head%q.size.Get()]
q.head.Set(head + 1)
return data, true
}
// Enq enqueues the given element data at the back of the receiver FIFO and
// returns true.
// If the FIFO is full and no element can be enqueued, returns false.
//
// TODO(ardnew): Document both operations based on receiver's QueueFullMode.
func (q *Queue) Enq(data uint8) bool {
tail := q.tail.Get()
head := q.head.Get()
if tail-head == q.size.Get() {
switch q.mode {
case QueueFullDiscardLast:
// drop incoming data
return false
case QueueFullDiscardFirst:
// drop outgoing data
q.head.Set(head + 1)
}
} // full queue
(*q.fifo)[tail%q.size.Get()] = data
q.tail.Set(tail + 1)
return true
}
// Read implements the io.Reader interface. It dequeues min(q.Len(), len(data))
// elements from the receiver FIFO into the given slice data.
// If len(data) equals 0, returns 0 and ErrReadBuffer.
// Otherwise, if q.Len() equals 0, returns 0 and ErrQueueEmpty.
func (q *Queue) Read(data []uint8) (int, error) {
less := uint32(len(data))
if less == 0 {
return 0, ErrQueueReadZero
} // nothing to copy into
head := q.head.Get()
used := q.tail.Get() - head
if used == 0 {
return 0, ErrQueueEmpty
} // empty queue
if less > used {
less = used
} // only get from used space
for i := uint32(0); i < less; i++ {
data[i] = (*q.fifo)[head%q.size.Get()]
head++
}
q.head.Set(head)
return int(less), nil
}
// Write implements the io.Writer interface. It enqueues min(q.Rem(), len(data))
// elements from the given slice data into the receiver FIFO.
// If len(data) equals 0, returns 0 and ErrWriteBuffer.
// Otherwise, if q.Rem() equals 0, returns 0 and ErrQueueFull.
//
// TODO(ardnew): Document both operations based on receiver's QueueFullMode.
func (q *Queue) Write(data []uint8) (int, error) {
more := uint32(len(data))
// Nothing to copy from is an error regardless of mode.
if more == 0 {
return 0, ErrQueueWriteZero
}
switch q.mode {
case QueueFullDiscardLast:
// drop incoming data
tail := q.tail.Get()
used := tail - q.head.Get()
// Full queue, cannot add any data.
if used == q.size.Get() {
return 0, ErrQueueFull
}
// Only put to unused space.
if used+more > q.size.Get() {
more = q.size.Get() - used
}
// Copy a potentially-limited number of elements from data, depending on the
// current length of FIFO.
for i := uint32(0); i < more; i++ {
(*q.fifo)[tail%q.size.Get()] = data[i]
tail++
}
q.tail.Set(tail)
return int(more), nil
case QueueFullDiscardFirst:
// drop outgoing data
// Trying to write more data than the FIFO will hold will simply overwrite
// some of the given data, so there is no point writing that data.
from := uint32(0)
if more >= q.size.Get() {
// Begin copying only the data that will be kept.
from = more - q.size.Get()
// We can fill the entire FIFO.
more = q.size.Get()
// Reset the indices
q.head.Set(0)
q.tail.Set(0)
}
tail := q.tail.Get()
used := tail - q.head.Get()
// Make space for incoming data by discarding only as many FIFO elements as
// is necessary to store incoming data.
if used+more > q.size.Get() {
q.head.Set(tail + more - q.size.Get())
}
// Copy a potentially-limited number of elements from data, depending on the
// current length of FIFO.
for i := uint32(0); i < more; i++ {
(*q.fifo)[tail%q.size.Get()] = data[from+i]
tail++
}
q.tail.Set(tail)
return int(more), nil
}
return 0, ErrQueueDiscardMode
}
// Front returns the next element that would be dequeued from the receiver FIFO
// and true.
// If the FIFO is empty and no element would be dequeued, returns 0 and false.
func (q *Queue) Front() (uint8, bool) {
head := q.head.Get()
if head == q.tail.Get() {
return 0, false
} // empty queue
return (*q.fifo)[head%q.size.Get()], true
}
// Back returns the last element that would be dequeued from the receiver FIFO
// and true.
// If the FIFO is empty and no element would be dequeued, returns 0 and false.
func (q *Queue) Back() (uint8, bool) {
tail := q.tail.Get()
if tail == q.head.Get() {
return 0, false
} // empty queue
return (*q.fifo)[(tail-1)%q.size.Get()], true
}
// index returns an index into the receiver FIFO based on sign and magnitude of i:
// 1. If i is greater than or equal to zero and less then q.Len(), returns the
// (i+1)'th element that would be dequeued from the receiver FIFO and true.
// 2. Otherwise, if i is negative and -i is less than or equal to q.Len(), returns
// the -(i+1)'th from the last element that would be dequeued from the receiver
// FIFO and true.
// 3. Otherwise, returns 0 and false.
func (q *Queue) index(i int) (int, bool) {
if n := q.Len(); i < 0 {
if -i <= n {
return (int(q.tail.Get()) + i) % int(q.size.Get()), true
}
} else {
if i < n {
return (int(q.head.Get()) + i) % int(q.size.Get()), true
}
}
return 0, false
}
// Get returns the value of an element in the receiver FIFO, offset by i from the
// front of the queue if i is positive, or from the back of the queue if i is
// negative. For example:
// Get(0) == Get(-Len()) == Front(), and
// Get(-1) == Get(Len()-1) == Back().
// If the offset is beyond queue boundaries, returns 0 and false.
func (q *Queue) Get(i int) (uint8, bool) {
if n, ok := q.index(i); ok {
return (*q.fifo)[n], true
}
return 0, false
}
// Set modifies the value of an element in the receiver FIFO.
// Set uses the same logic as Get to select an element in the FIFO.
func (q *Queue) Set(i int, data uint8) bool {
if n, ok := q.index(i); ok {
(*q.fifo)[n] = data
return true
}
return false
}
-83
View File
@@ -1,83 +0,0 @@
//go:build usb.cdc
// +build usb.cdc
package usb
import (
"errors"
)
var (
ErrCDCInvalidPort = errors.New("invalid port")
ErrCDCEmptyBuffer = errors.New("buffer empty")
)
// CDC represents a virtual UART serial device emulation using the USB
// CDC-ACM device class driver.
type CDC struct {
// Port is the MCU's native USB core number. If in doubt, leave it
// uninitialized for default (0).
Port int
core *core
}
type CDCConfig struct {
BusSpeed Speed
}
func (cdc *CDC) Configure(config CDCConfig) error {
c := class{id: classDeviceCDC, config: 1}
// verify we have a free USB port and take ownership of it
var st status
cdc.core, st = initCore(cdc.Port, config.BusSpeed, c)
if !st.ok() {
return ErrCDCInvalidPort
}
return nil
}
func (cdc *CDC) Ready() bool {
return cdc.core.dc.cdcReady()
}
// Buffered returns the number of bytes currently stored in the Rx buffer.
func (cdc *CDC) Buffered() int {
for !cdc.Ready() {
}
return cdc.core.dc.cdcAvailable()
}
// ReadByte reads a single byte from the Rx buffer.
// If there is no data in the buffer, returns an error.
func (cdc *CDC) ReadByte() (byte, error) {
for !cdc.Ready() {
}
n, ok := cdc.core.dc.cdcReadByte()
if !ok {
return 0, ErrCDCEmptyBuffer
}
return n, nil
}
// Read from the Rx buffer.
func (cdc *CDC) Read(data []byte) (n int, err error) {
for !cdc.Ready() {
}
return cdc.core.dc.cdcRead(data)
}
// WriteByte writes a single byte of data to the virtual UART interface.
func (cdc *CDC) WriteByte(c byte) error {
for !cdc.Ready() {
}
return cdc.core.dc.cdcWriteByte(c)
}
// Write data to the virtual UART.
func (cdc *CDC) Write(data []byte) (n int, err error) {
for !cdc.Ready() {
}
return cdc.core.dc.cdcWrite(data)
}
-48
View File
@@ -1,48 +0,0 @@
//go:build usb.hid
// +build usb.hid
package usb
import (
"errors"
)
var (
ErrHIDInvalidPort = errors.New("invalid USB port")
ErrHIDInvalidCore = errors.New("invalid USB core")
ErrHIDReportTransfer = errors.New("failed to transfer HID report")
)
// HID represents a virtual keyboard/mouse/joystick device (with a serial data
// Rx/Tx interface) using the USB HID device class driver.
type HID struct {
// Port is the MCU's native USB core number. If in doubt, leave it
// uninitialized for default (0).
Port int
core *core
}
type HIDConfig struct {
BusSpeed Speed
}
func (hid *HID) Configure(config HIDConfig) error {
c := class{id: classDeviceHID, config: 1}
// verify we have a free USB port and take ownership of it
var st status
hid.core, st = initCore(hid.Port, config.BusSpeed, c)
if !st.ok() {
return ErrHIDInvalidPort
}
return nil
}
func (hid *HID) Ready() bool {
return hid.core.dc.keyboard().ready()
}
func (hid *HID) Keyboard() *Keyboard {
return hid.core.dc.keyboard()
}
-158
View File
@@ -1,158 +0,0 @@
package usb
// Hardware abstraction for USB ports configured as either host or device.
// CoreCount defines the total number of USB cores which may be configured in
// device or host mode.
const CoreCount = descCoreCount
// coreInstance provides statically-allocated instances of each USB core
// configured on this platform.
var coreInstance [CoreCount]core
// core represents the core of a USB port configured as either host or device.
type core struct {
port int
mode int
dc *dcd
hc *hcd
}
// Constant definitions for USB core operating modes.
const (
modeIdle = 0 // USB port has not been configured
modeDevice = 1
modeHost = 2
)
// Speed represents the configured USB data transfer rate, or bus speed, for
// communication between host and device.
type Speed uint8
// Constant definitions for USB data transfer rates. Note that every transfer
// rate may not be supported by every target due to either hardware or software
// limitations. By far, the most commonly-supported rate is USB 1.1 Full-Speed
// (12 Mbit/sec). If unsure, either use FullSpeed or leave it undefined and let
// the driver use the default speed for your target.
const (
LowSpeed Speed = iota + 1 // 1.5 Mbit/sec (USB 1.0)
FullSpeed // 12 Mbit/sec (USB 1.1)
HighSpeed // 480 Mbit/sec (USB 2.0)
SuperSpeed // 5 Gbit/sec (USB 3.0)
DualSuperSpeed // 10 Gbit/sec (USB 3.1, Dual-Lane SS)
)
// initCore initializes a free USB core with given operating mode on the USB
// port at given index, if available. Returns a reference to the initialized
// core or nil if the core is unavailable.
func initCore(port int, speed Speed, class class) (*core, status) {
if port < 0 || port >= CoreCount || 0 == class.config {
return nil, statusInvalid
}
if modeIdle != coreInstance[port].mode {
// Check if requested port is already configured as requested class. If so,
// just return a reference to the existing core instead of an error.
//
// This will allow, for instance, TinyGo examples that try to reconfigure
// the USB (CDC-ACM) UART port (which is already configured by the runtime)
// to continue without error.
if coreInstance[port].mode == class.mode() {
switch class.mode() {
case modeDevice:
if coreInstance[port].dc.class().equals(class) {
return &coreInstance[port], statusOK
}
case modeHost:
if coreInstance[port].hc.class().equals(class) {
return &coreInstance[port], statusOK
}
}
}
return nil, statusBusy
}
switch class.mode() {
case modeDevice:
// Allocate a free device controller and install interrupts
dc, st := initDCD(port, speed, class)
if !st.ok() {
return nil, st
}
// Initialize buffers and device descriptors
if st = dc.init(); !st.ok() {
return nil, st
}
coreInstance[port].port = port
coreInstance[port].mode = modeDevice
coreInstance[port].dc = dc
dc.enable(true) // Enable interrupts and enter runtime
case modeHost:
// Allocate a free host controller and install interrupts
hc, st := initHCD(port, speed, class)
if !st.ok() {
return nil, st
}
// Initialize buffers and device descriptors
if st = hc.init(); !st.ok() {
return nil, st
}
coreInstance[port].port = port
coreInstance[port].mode = modeHost
coreInstance[port].hc = hc
hc.enable(true) // Enable interrupts and enter runtime
default:
return nil, statusInvalid
}
return &coreInstance[port], statusOK
}
// class represents the type of a host/device and its class configuration index.
// The first valid configuration index is 1. Index 0 is reserved and invalid.
type class struct {
id int
config int
}
// Enumerated constants for all supported host/device class configurations.
const (
classDeviceCDC = 0
classDeviceHID = 1
)
// mode returns the USB core operating mode of the receiver class c.
//go:inline
func (c class) mode() int {
switch c.id {
case classDeviceCDC, classDeviceHID:
return modeDevice
default:
return modeIdle
}
}
// equals returns true if and only if all fields of the given class are equal to
// those of the receiver c.
//go:inline
func (c class) equals(class class) bool {
return c.id == class.id && c.config == class.config
}
// status represents the return code of a subroutine.
type status uint8
// Constant definitions for all status codes used within the package.
const (
statusOK status = iota // Success
statusBusy // Busy
statusInvalid // Invalid argument
statusFail // Failure
)
// ok returns true if and only if the receiver st equals statusOK.
//go:inline
func (s status) ok() bool { return statusOK == s }
-341
View File
@@ -1,341 +0,0 @@
package usb
//go:linkname ticks runtime.ticks
func ticks() int64
// leU64 returns a slice containing 8 bytes from the given uint64 u.
//
// The returned bytes have little-endian ordering; that is, the first element
// at index 0 is the least-significant byte in u and index 7 is the most-
// significant byte.
//go:inline
func leU64(u uint64) []uint8 {
var b [8]uint8
if u == 0 {
// skip all processing for the common case (u = 0)
return b[:]
}
b[0] = uint8(u)
b[1] = uint8(u >> 8)
b[2] = uint8(u >> 16)
b[3] = uint8(u >> 24)
b[4] = uint8(u >> 32)
b[5] = uint8(u >> 40)
b[6] = uint8(u >> 48)
b[7] = uint8(u >> 56)
return b[:]
}
// leU32 returns a slice containing 4 bytes from the given uint32 u.
//
// The returned bytes have little-endian ordering; that is, the first element
// at index 0 is the least-significant byte in u and index 3 is the most-
// significant byte.
//go:inline
func leU32(u uint32) []uint8 {
var b [4]uint8
if u == 0 {
// skip all processing for the common case (u = 0)
return b[:]
}
b[0] = uint8(u)
b[1] = uint8(u >> 8)
b[2] = uint8(u >> 16)
b[3] = uint8(u >> 24)
return b[:]
}
// leU16 returns a slice containing 2 bytes from the given uint16 u.
//
// The returned bytes have little-endian ordering; that is, the first element
// at index 0 is the least-significant byte in u and index 1 is the most-
// significant byte.
//go:inline
func leU16(u uint16) []uint8 {
var b [2]uint8
if u == 0 {
// skip all processing for the common case (u = 0)
return b[:]
}
b[0] = uint8(u)
b[1] = uint8(u >> 8)
return b[:]
}
// beU64 returns a slice containing 8 bytes from the given uint64 u.
//
// The returned bytes have big-endian ordering; that is, the first element at
// index 0 is the most-significant byte in u and index 7 is the least-
// significant byte.
//go:inline
func beU64(u uint64) []uint8 {
var b [8]uint8
if u == 0 {
// skip all processing for the common case (u = 0)
return b[:]
}
b[7] = uint8(u)
b[6] = uint8(u >> 8)
b[5] = uint8(u >> 16)
b[4] = uint8(u >> 24)
b[3] = uint8(u >> 32)
b[2] = uint8(u >> 40)
b[1] = uint8(u >> 48)
b[0] = uint8(u >> 56)
return b[:]
}
// beU32 returns a slice containing 4 bytes from the given uint32 u.
//
// The returned bytes have big-endian ordering; that is, the first element at
// index 0 is the most-significant byte in u and index 3 is the least-
// significant byte.
//go:inline
func beU32(u uint32) []uint8 {
var b [4]uint8
if u == 0 {
// skip all processing for the common case (u = 0)
return b[:]
}
b[3] = uint8(u)
b[2] = uint8(u >> 8)
b[1] = uint8(u >> 16)
b[0] = uint8(u >> 24)
return b[:]
}
// beU16 returns a slice containing 2 bytes from the given uint16 u.
//
// The returned bytes have big-endian ordering; that is, the first element at
// index 0 is the most-significant byte in u and index 1 is the least-
// significant byte.
//go:inline
func beU16(u uint16) []uint8 {
var b [2]uint8
if u == 0 {
// skip all processing for the common case (u = 0)
return b[:]
}
b[1] = uint8(u)
b[0] = uint8(u >> 8)
return b[:]
}
// revU64 returns the given uint64 u with bytes in the reverse order.
//go:inline
func revU64(u uint64) uint64 {
if u == 0 {
// skip all processing for the common case (u = 0)
return 0
}
return ((u & 0x00000000000000FF) << 56) |
((u & 0x000000000000FF00) << 40) |
((u & 0x0000000000FF0000) << 24) |
((u & 0x00000000FF000000) << 8) |
((u & 0x000000FF00000000) >> 8) |
((u & 0x0000FF0000000000) >> 24) |
((u & 0x00FF000000000000) >> 40) |
((u & 0xFF00000000000000) >> 56)
}
// revU32 returns the given uint32 u with bytes in the reverse order.
//go:inline
func revU32(u uint32) uint32 {
if u == 0 {
// skip all processing for the common case (u = 0)
return 0
}
return ((u & 0x000000FF) << 24) | ((u & 0x0000FF00) << 8) |
((u & 0x00FF0000) >> 8) | ((u & 0xFF000000) >> 24)
}
// revU16 returns the given uint16 u with bytes in the reverse order.
//go:inline
func revU16(u uint16) uint16 {
if u == 0 {
// skip all processing for the common case (u = 0)
return 0
}
return ((u & 0x00FF) << 8) | ((u & 0xFF00) >> 8)
}
// packU64 returns a uint64 constructed by concatenating the bytes in slice b.
//
// The least-significant byte in the returned value is the first element at
// index 0 in b and the most significant byte is index 7, if given. If fewer
// than 8 elements are given in b, the corresponding bytes in the returned value
// are all 0.
//go:inline
func packU64(b []uint8) (u uint64) {
for i := 0; i < 8 && i < len(b); i++ {
u |= uint64(b[i]) << (i * 8)
}
return
}
// packU32 returns a uint32 constructed by concatenating the bytes in slice b.
//
// The least-significant byte in the returned value is the first element at
// index 0 in b and the most significant byte is index 3, if given. If fewer
// than 4 elements are given in b, the corresponding bytes in the returned value
// are all 0.
//go:inline
func packU32(b []uint8) (u uint32) {
for i := 0; i < 4 && i < len(b); i++ {
u |= uint32(b[i]) << (i * 8)
}
return
}
// packU16 returns a uint16 constructed by concatenating the bytes in slice b.
//
// The least-significant byte in the returned value is the first element at
// index 0 in b and the most significant byte is index 1, if given. If fewer
// than 2 elements are given in b, the corresponding bytes in the returned value
// are all 0.
//go:inline
func packU16(b []uint8) (u uint16) {
for i := 0; i < 2 && i < len(b); i++ {
u |= uint16(b[i]) << (i * 8)
}
return
}
// msU8 returns the most-significant byte of u.
//go:inline
func msU8(u uint16) uint8 { return uint8(u >> 8) }
// lsU8 returns the least-significant byte of u.
//go:inline
func lsU8(u uint16) uint8 { return uint8(u) }
// cycles converts the given number of microseconds to CPU cycles for a CPU with
// given frequency.
//go:inline
func cycles(microsec, cpuFreqHz uint32) uint32 {
return uint32((uint64(microsec) * uint64(cpuFreqHz)) / 1000000)
}
//go:inline
func endpointValid(address uint8) bool {
return address&descEndpointInvalid == 0
}
//go:inline
func unpackEndpoint(address uint8) (number, direction uint8) {
return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos,
(address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos
}
//go:inline
func packEndpoint(number, direction uint8) (address uint8) {
return ((number << descEndptAddrNumberPos) & descEndptAddrNumberMsk) |
((direction << descEndptAddrDirectionPos) & descEndptAddrDirectionMsk)
}
//go:inline
func endpointNumber(address uint8) (number uint8) {
return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos
}
//go:inline
func endpointDirection(address uint8) (direction uint8) {
return (address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos
}
//go:inline
func rxEndpoint(number uint8) uint8 {
return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionOut
}
//go:inline
func txEndpoint(number uint8) uint8 {
return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionIn
}
//go:inline
func endpointIndex(address uint8) uint8 {
return ((address & descEndptAddrNumberMsk) << 1) |
((address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos)
}
//go:inline
func indexEndpoint(index uint8) uint8 {
return ((index >> 1) & descEndptAddrNumberMsk) |
((index & 0x1) << descEndptAddrDirectionPos)
}
// wrap computes the index into a circular buffer of length mod by walking
// forward n elements if n is positive, or reverse -n elements if n is negative.
// For example, both wrap(12, 10) and wrap(-18, 10) return 2.
//go:inline
func wrap(n, mod int) int {
if mod <= 0 {
// Buffer length (mod) must be positive.
return 0
}
if n < 0 {
if -n < mod {
// Do not wrap around (no underflow).
return mod + n
}
return mod - (-n % mod)
}
if n < mod {
// Do not wrap around (no overflow).
return n
}
return n % mod
}
// The following buffLo and buffHi are helper methods for slice definitions from
// potentially zero-length arrays (depending on compile-time constants).
//
// For example, if we have an array containing a 5-element buffer for three
// instances of some device class (15 total elements), partitioned as follows,
// then we compute the indices for instance 2 as usual:
//
// Index: 01234 56789 ABCDE
// Array: [ 1 | 2 | 3 ]
//
// Lo: (n-1) * size => (2-1) * 5 => 5
// Hi: (n) * size => (2) * 5 => 10 (0xA)
//
// However, if we have specified (via const definition) that 0 instances of some
// device class be allocated, then the associated device class buffer arrays
// will all be zero-length arrays, and the arithmetic to compute the slice
// indices used above will result in out-of-bounds indices:
//
// Index:
// Array: []
//
// Lo: (n-1) * size => (2-1) * 5 => 5 [Error!]
// Hi: (n) * size => (2) * 5 => 10 (0xA) [Error!]
//
//
// I couldn't figure out a straight-forward way to resolve these slice indices
// using only arithmetic, so I've resorted to simple conditionals. If the number
// of instances for some given class is zero (count=0), defined via compile-time
// constant, then just use the empty slice range [0:0].
// buffLo returns the starting array slice index for the n'th region of size
// elements from an array containing count regions of size elements.
// Regions are specified using a 1-based index (n > 0). Returns 0 if any given
// argument equals 0.
func buffLo(n, count, size uint16) uint16 {
if 0 == n || 0 == count || 0 == size {
return 0
}
return (n - 1) * size
}
// buffHi returns the ending array slice index for the n'th region of size
// elements from an array containing count regions of size elements.
// Regions are specified using a 1-based index (n > 0). Returns 0 if any given
// argument equals 0.
func buffHi(n, count, size uint16) uint16 {
if 0 == n || 0 == count || 0 == size {
return 0
}
return n * size
}
-24
View File
@@ -1,24 +0,0 @@
// +build arm
package usb
import "device/arm"
// udelay waits for the given number of microseconds before returning.
// We cannot use the sleep timer from this context (import cycle), but we need
// an approximate method to spin CPU cycles for short periods of time.
//go:inline
func udelay(microsec uint32) {
n := cycles(microsec, descCPUFrequencyHz)
for i := uint32(0); i < n; i++ {
arm.Asm(`nop`)
}
}
func disableInterrupts() uintptr {
return arm.DisableInterrupts()
}
func enableInterrupts(mask uintptr) {
arm.EnableInterrupts(mask)
}
+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)
+18 -8
View File
@@ -7,6 +7,7 @@ import (
"device/arm"
"device/sam"
"machine"
"machine/usb/cdc"
"runtime/interrupt"
"runtime/volatile"
)
@@ -25,22 +26,31 @@ func init() {
initClocks()
initRTC()
initSERCOMClocks()
initUSBClock()
initADCClock()
//// connect to USB CDC interface
//machine.Serial.Configure(usb.UARTConfig{})
//if !machine.USB.Configured() {
// machine.USB.Configure(usb.UARTConfig{})
//}
machine.InitUSB()
machine.InitSerial()
// connect to USB CDC interface
cdc.EnableUSBCDC()
machine.USB.Configure(machine.UARTConfig{})
machine.Serial.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-- {
@@ -36,7 +36,7 @@ func handleHardFault(sp *interruptStack) {
if fault.Mem().WhileUnstackingException() {
print(" while unstacking exception")
}
if fault.Mem().WhileStackingException() {
if fault.Mem().WileStackingException() {
print(" while stacking exception")
}
if fault.Mem().DuringFPLazyStatePres() {
@@ -162,13 +162,13 @@ func (fs MemFaultStatus) WhileUnstackingException() bool {
return fs&arm.SCB_CFSR_MUNSTKERR != 0
}
// WhileStackingException: stacking for an exception entry has caused one or more
// WileStackingException: stacking for an exception entry has caused one or more
// access violations
//
// "When this bit is 1, the SP is still adjusted but the values in the context
// area on the stack might be incorrect. The processor has not written a fault
// address to the MMAR."
func (fs MemFaultStatus) WhileStackingException() bool {
func (fs MemFaultStatus) WileStackingException() bool {
return fs&arm.SCB_CFSR_MSTKERR != 0
}
+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")
}

Some files were not shown because too many files have changed in this diff Show More