mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 03:27:48 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eafca3c583 | |||
| a01c3423a1 | |||
| 8754f64f3b | |||
| caf405b01d | |||
| bb65c5ce2b | |||
| 283fed16a5 | |||
| 76bba13963 | |||
| ada11090a2 | |||
| a07287d3c6 | |||
| 1b2e764835 | |||
| 2c93a4085c | |||
| 906757603d | |||
| 5d16811199 | |||
| f2e576decf | |||
| 2d61972475 | |||
| 5c488e3145 | |||
| e45ff9c0e8 | |||
| 39805bca45 | |||
| 97842b367c | |||
| 9246899b30 | |||
| 04ace4de5f |
@@ -377,6 +377,8 @@ tinygo-baremetal:
|
||||
.PHONY: smoketest
|
||||
smoketest:
|
||||
$(TINYGO) version
|
||||
# regression test for #2892
|
||||
cd tests/testing/recurse && ($(TINYGO) test ./... > recurse.log && cat recurse.log && test $$(wc -l < recurse.log) = 2 && rm recurse.log)
|
||||
# compile-only platform-independent examples
|
||||
cd tests/text/template/smoke && $(TINYGO) test -c && rm -f smoke.test
|
||||
# regression test for #2563
|
||||
@@ -396,6 +398,8 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/echo2
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/i2s
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/mcp3008
|
||||
@@ -412,6 +416,10 @@ smoketest:
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=pca10040 examples/test
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-mouse
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=wioterminal examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
# test simulated boards on play.tinygo.org
|
||||
ifneq ($(WASM), 0)
|
||||
$(TINYGO) build -size short -o test.wasm -tags=arduino examples/blinky1
|
||||
@@ -553,6 +561,11 @@ endif
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
# test usbhid
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-nrf52840 examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=circuitplay-express examples/hid-keyboard
|
||||
@$(MD5SUM) test.hex
|
||||
ifneq ($(STM32), 0)
|
||||
$(TINYGO) build -size short -o test.hex -target=bluepill examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
|
||||
+10
-37
@@ -193,7 +193,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{pkgName}, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, pkgName, config.ClangHeaders, types.Config{
|
||||
Sizes: compiler.Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -439,27 +439,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return errors.New("verification error after interpreting " + pkgInit.Name())
|
||||
}
|
||||
|
||||
// Run function passes for each function in the module.
|
||||
// These passes are intended to be run on each function right
|
||||
// after they're created to reduce IR size (and maybe also for
|
||||
// cache locality to improve performance), but for now they're
|
||||
// run here for each function in turn. Maybe this can be
|
||||
// improved in the future.
|
||||
builder := llvm.NewPassManagerBuilder()
|
||||
defer builder.Dispose()
|
||||
builder.SetOptLevel(optLevel)
|
||||
builder.SetSizeLevel(sizeLevel)
|
||||
funcPasses := llvm.NewFunctionPassManagerForModule(mod)
|
||||
defer funcPasses.Dispose()
|
||||
builder.PopulateFunc(funcPasses)
|
||||
funcPasses.InitializeFunc()
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
if fn.IsDeclaration() {
|
||||
continue
|
||||
}
|
||||
funcPasses.RunFunc(fn)
|
||||
}
|
||||
funcPasses.FinalizeFunc()
|
||||
transform.OptimizePackage(mod, config)
|
||||
|
||||
// Serialize the LLVM module as a bitcode file.
|
||||
// Write to a temporary path that is renamed to the destination
|
||||
@@ -729,6 +709,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
return fmt.Errorf("stripping debug information is unnecessary for baremetal targets")
|
||||
}
|
||||
}
|
||||
if config.GOOS() == "darwin" {
|
||||
// Debug information isn't stored in the binary itself on MacOS but
|
||||
// is left in the object files by default. The binary does store the
|
||||
// path to these object files though.
|
||||
return errors.New("cannot remove debug information: MacOS doesn't store debug info in the executable by default")
|
||||
}
|
||||
if config.Target.Linker == "wasm-ld" {
|
||||
// Don't just strip debug information, also compress relocations
|
||||
// while we're at it. Relocations can only be compressed when debug
|
||||
@@ -738,21 +724,8 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||
// ld.lld is also used on Linux.
|
||||
ldflags = append(ldflags, "--strip-debug")
|
||||
} else {
|
||||
switch config.GOOS() {
|
||||
case "linux":
|
||||
// Either real linux or an embedded system (like AVR) that
|
||||
// pretends to be Linux. It's a ELF linker wrapped by GCC in any
|
||||
// case (not ld.lld - that case is handled above).
|
||||
ldflags = append(ldflags, "-Wl,--strip-debug")
|
||||
case "darwin":
|
||||
// MacOS (darwin) doesn't have a linker flag to strip debug
|
||||
// information. Apple expects you to use the strip command
|
||||
// instead.
|
||||
return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag")
|
||||
default:
|
||||
// Other OSes may have different flags.
|
||||
return errors.New("cannot remove debug information: unknown OS: " + config.GOOS())
|
||||
}
|
||||
// Other linkers may have different flags.
|
||||
return errors.New("cannot remove debug information: unknown linker: " + config.Target.Linker)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ var Musl = Library{
|
||||
"internal/libc.c",
|
||||
"internal/syscall_ret.c",
|
||||
"internal/vdso.c",
|
||||
"legacy/*.c",
|
||||
"malloc/*.c",
|
||||
"mman/*.c",
|
||||
"signal/*.c",
|
||||
|
||||
@@ -21,6 +21,10 @@ import (
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
var typeParamUnderlyingType = func(t types.Type) types.Type {
|
||||
return t
|
||||
}
|
||||
|
||||
func init() {
|
||||
llvm.InitializeAllTargets()
|
||||
llvm.InitializeAllTargetMCs()
|
||||
@@ -335,6 +339,7 @@ func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
|
||||
// makeLLVMType creates a LLVM type for a Go type. Don't call this, use
|
||||
// getLLVMType instead.
|
||||
func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
|
||||
goType = typeParamUnderlyingType(goType)
|
||||
switch typ := goType.(type) {
|
||||
case *types.Array:
|
||||
elemType := c.getLLVMType(typ.Elem())
|
||||
@@ -444,6 +449,7 @@ func (c *compilerContext) getDIType(typ types.Type) llvm.Metadata {
|
||||
// createDIType creates a new DWARF type. Don't call this function directly,
|
||||
// call getDIType instead.
|
||||
func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
|
||||
typ = typeParamUnderlyingType(typ)
|
||||
llvmType := c.getLLVMType(typ)
|
||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||
switch typ := typ.(type) {
|
||||
@@ -794,6 +800,9 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package
|
||||
for _, method := range methods {
|
||||
// Parse this method.
|
||||
fn := pkg.Prog.MethodValue(method)
|
||||
if fn == nil {
|
||||
continue // probably a generic method
|
||||
}
|
||||
if fn.Blocks == nil {
|
||||
continue // external function
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package compiler
|
||||
|
||||
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
|
||||
// support.
|
||||
|
||||
import "go/types"
|
||||
|
||||
func init() {
|
||||
typeParamUnderlyingType = func(t types.Type) types.Type {
|
||||
if t, ok := t.(*types.TypeParam); ok {
|
||||
return t.Underlying()
|
||||
}
|
||||
return t
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func TestCompiler(t *testing.T) {
|
||||
defer machine.Dispose()
|
||||
|
||||
// Load entire program AST into memory.
|
||||
lprogram, err := loader.Load(config, []string{"./testdata/" + tc.file}, config.ClangHeaders, types.Config{
|
||||
lprogram, err := loader.Load(config, "./testdata/"+tc.file, config.ClangHeaders, types.Config{
|
||||
Sizes: Sizes(machine),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
+1
-1
@@ -191,7 +191,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) llvm.Value {
|
||||
// should be created right away.
|
||||
// The exception is the package initializer, which does appear in the
|
||||
// *ssa.Package members and so shouldn't be created here.
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
|
||||
if fn.Synthetic != "" && fn.Synthetic != "package initializer" && fn.Synthetic != "generic function" {
|
||||
irbuilder := c.ctx.NewBuilder()
|
||||
b := newBuilder(c, irbuilder, fn)
|
||||
b.createFunction()
|
||||
|
||||
@@ -13,7 +13,7 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
go.bug.st/serial v1.1.3
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9
|
||||
golang.org/x/tools v0.1.11
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
tinygo.org/x/go-llvm v0.0.0-20220420140351-512c94c1e71f
|
||||
)
|
||||
|
||||
@@ -40,41 +40,40 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.bug.st/serial v1.1.3 h1:YEBxJa9pKS9Wdg46B/jiaKbvvbUrjhZZZITfJHEJhaE=
|
||||
go.bug.st/serial v1.1.3/go.mod h1:8TT7u/SwwNIpJ8QaG4s+HTjFt9ReXs2cdOU7ZEk50Dk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9 h1:nvvuMxmx1q0gfRki3T0hjG8EwAcVCs91oWAXvyt4zhI=
|
||||
golang.org/x/tools v0.1.6-0.20210813165731-45389f592fe9/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
+4
-1
@@ -18,6 +18,7 @@ var (
|
||||
errUnsupportedInst = errors.New("interp: unsupported instruction")
|
||||
errUnsupportedRuntimeInst = errors.New("interp: unsupported instruction (to be emitted at runtime)")
|
||||
errMapAlreadyCreated = errors.New("interp: map already created")
|
||||
errLoopUnrolled = errors.New("interp: loop unrolled")
|
||||
)
|
||||
|
||||
// This is one of the errors that can be returned from toLLVMValue when the
|
||||
@@ -26,7 +27,9 @@ var (
|
||||
var errInvalidPtrToIntSize = errors.New("interp: ptrtoint integer size does not equal pointer size")
|
||||
|
||||
func isRecoverableError(err error) bool {
|
||||
return err == errIntegerAsPointer || err == errUnsupportedInst || err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated
|
||||
return err == errIntegerAsPointer || err == errUnsupportedInst ||
|
||||
err == errUnsupportedRuntimeInst || err == errMapAlreadyCreated ||
|
||||
err == errLoopUnrolled
|
||||
}
|
||||
|
||||
// ErrorLine is one line in a traceback. The position may be missing.
|
||||
|
||||
@@ -24,15 +24,39 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
locals[i] = param
|
||||
}
|
||||
|
||||
// Track what blocks have run instructions at runtime.
|
||||
// This is used to prevent unrolling.
|
||||
var runtimeBlocks map[int]struct{}
|
||||
|
||||
// Start with the first basic block and the first instruction.
|
||||
// Branch instructions may modify both bb and instIndex when branching.
|
||||
bb := fn.blocks[0]
|
||||
currentBB := 0
|
||||
lastBB := -1 // last basic block is undefined, only defined after a branch
|
||||
var operands []value
|
||||
startRTInsts := len(mem.instructions)
|
||||
for instIndex := 0; instIndex < len(bb.instructions); instIndex++ {
|
||||
if instIndex == 0 {
|
||||
// This is the start of a new basic block.
|
||||
if len(mem.instructions) != startRTInsts {
|
||||
if _, ok := runtimeBlocks[lastBB]; ok {
|
||||
// This loop has been unrolled.
|
||||
// Avoid doing this, as it can result in a large amount of extra machine code.
|
||||
// This currently uses the branch from the last block, as there is no available information to give a better location.
|
||||
lastBBInsts := fn.blocks[lastBB].instructions
|
||||
return nil, mem, r.errorAt(lastBBInsts[len(lastBBInsts)-1], errLoopUnrolled)
|
||||
}
|
||||
|
||||
// Flag the last block as having run stuff at runtime.
|
||||
if runtimeBlocks == nil {
|
||||
runtimeBlocks = make(map[int]struct{})
|
||||
}
|
||||
runtimeBlocks[lastBB] = struct{}{}
|
||||
|
||||
// Reset the block-start runtime instructions counter.
|
||||
startRTInsts = len(mem.instructions)
|
||||
}
|
||||
|
||||
// There may be PHI nodes that need to be resolved. Resolve all PHI
|
||||
// nodes before continuing with regular instructions.
|
||||
// PHI nodes need to be treated specially because they can have a
|
||||
|
||||
Vendored
+45
@@ -3,6 +3,8 @@ target triple = "x86_64--linux"
|
||||
|
||||
declare void @externalCall(i64)
|
||||
|
||||
declare i64 @ptrHash(i8* nocapture)
|
||||
|
||||
@foo.knownAtRuntime = global i64 0
|
||||
@bar.knownAtRuntime = global i64 0
|
||||
@baz.someGlobal = external global [3 x {i64, i32}]
|
||||
@@ -10,6 +12,8 @@ declare void @externalCall(i64)
|
||||
@x.atomicNum = global i32 0
|
||||
@x.volatileNum = global i32 0
|
||||
@y.ready = global i32 0
|
||||
@z.bloom = global i64 0
|
||||
@z.arr = global [32 x i8] zeroinitializer
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
@@ -19,6 +23,7 @@ entry:
|
||||
call void @main.init(i8* undef)
|
||||
call void @x.init(i8* undef)
|
||||
call void @y.init(i8* undef)
|
||||
call void @z.init(i8* undef)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -72,3 +77,43 @@ loop:
|
||||
end:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @z.init(i8* %context) unnamed_addr {
|
||||
%bloom = bitcast i64* @z.bloom to i8*
|
||||
|
||||
; This can be safely expanded.
|
||||
call void @z.setArr(i8* %bloom, i64 1, i8* %bloom)
|
||||
|
||||
; This call should be reverted to prevent unrolling.
|
||||
call void @z.setArr(i8* bitcast ([32 x i8]* @z.arr to i8*), i64 32, i8* %bloom)
|
||||
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @z.setArr(i8* %arr, i64 %n, i8* %context) unnamed_addr {
|
||||
entry:
|
||||
br label %loop
|
||||
|
||||
loop:
|
||||
%prev = phi i64 [ %n, %entry ], [ %idx, %loop ]
|
||||
%idx = sub i64 %prev, 1
|
||||
%elem = getelementptr i8, i8* %arr, i64 %idx
|
||||
call void @z.set(i8* %elem, i8* %context)
|
||||
%done = icmp eq i64 %idx, 0
|
||||
br i1 %done, label %end, label %loop
|
||||
|
||||
end:
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal void @z.set(i8* %ptr, i8* %context) unnamed_addr {
|
||||
; Insert the pointer into the Bloom filter.
|
||||
%hash = call i64 @ptrHash(i8* %ptr)
|
||||
%index = lshr i64 %hash, 58
|
||||
%bit = shl i64 1, %index
|
||||
%bloom = bitcast i8* %context to i64*
|
||||
%old = load i64, i64* %bloom
|
||||
%new = or i64 %old, %bit
|
||||
store i64 %new, i64* %bloom
|
||||
ret void
|
||||
}
|
||||
|
||||
Vendored
+33
@@ -8,9 +8,13 @@ target triple = "x86_64--linux"
|
||||
@x.atomicNum = local_unnamed_addr global i32 0
|
||||
@x.volatileNum = global i32 0
|
||||
@y.ready = local_unnamed_addr global i32 0
|
||||
@z.bloom = global i64 0
|
||||
@z.arr = global [32 x i8] zeroinitializer
|
||||
|
||||
declare void @externalCall(i64) local_unnamed_addr
|
||||
|
||||
declare i64 @ptrHash(i8* nocapture) local_unnamed_addr
|
||||
|
||||
define void @runtime.initAll() unnamed_addr {
|
||||
entry:
|
||||
call fastcc void @baz.init(i8* undef)
|
||||
@@ -24,6 +28,8 @@ entry:
|
||||
%y = load volatile i32, i32* @x.volatileNum, align 4
|
||||
store volatile i32 %y, i32* @x.volatileNum, align 4
|
||||
call fastcc void @y.init(i8* undef)
|
||||
call fastcc void @z.set(i8* bitcast (i64* @z.bloom to i8*), i8* bitcast (i64* @z.bloom to i8*))
|
||||
call fastcc void @z.setArr(i8* getelementptr inbounds ([32 x i8], [32 x i8]* @z.arr, i32 0, i32 0), i64 32, i8* bitcast (i64* @z.bloom to i8*))
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -48,3 +54,30 @@ loop: ; preds = %loop, %entry
|
||||
end: ; preds = %loop
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal fastcc void @z.setArr(i8* %arr, i64 %n, i8* %context) unnamed_addr {
|
||||
entry:
|
||||
br label %loop
|
||||
|
||||
loop: ; preds = %loop, %entry
|
||||
%prev = phi i64 [ %n, %entry ], [ %idx, %loop ]
|
||||
%idx = sub i64 %prev, 1
|
||||
%elem = getelementptr i8, i8* %arr, i64 %idx
|
||||
call fastcc void @z.set(i8* %elem, i8* %context)
|
||||
%done = icmp eq i64 %idx, 0
|
||||
br i1 %done, label %end, label %loop
|
||||
|
||||
end: ; preds = %loop
|
||||
ret void
|
||||
}
|
||||
|
||||
define internal fastcc void @z.set(i8* %ptr, i8* %context) unnamed_addr {
|
||||
%hash = call i64 @ptrHash(i8* %ptr)
|
||||
%index = lshr i64 %hash, 58
|
||||
%bit = shl i64 1, %index
|
||||
%bloom = bitcast i8* %context to i64*
|
||||
%old = load i64, i64* %bloom, align 8
|
||||
%new = or i64 %old, %bit
|
||||
store i64 %new, i64* %bloom, align 8
|
||||
ret void
|
||||
}
|
||||
+7
-2
@@ -28,6 +28,8 @@ import (
|
||||
"github.com/tinygo-org/tinygo/goenv"
|
||||
)
|
||||
|
||||
var addInstances func(*types.Info)
|
||||
|
||||
// Program holds all packages and some metadata about the program as a whole.
|
||||
type Program struct {
|
||||
config *compileopts.Config
|
||||
@@ -104,7 +106,7 @@ type EmbedFile struct {
|
||||
// Load loads the given package with all dependencies (including the runtime
|
||||
// package). Call .Parse() afterwards to parse all Go files (including CGo
|
||||
// processing, if necessary).
|
||||
func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
||||
func Load(config *compileopts.Config, inputPkg string, clangHeaders string, typeChecker types.Config) (*Program, error) {
|
||||
goroot, err := GetCachedGoroot(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -133,7 +135,7 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
if config.TestConfig.CompileTestBinary {
|
||||
extraArgs = append(extraArgs, "-test")
|
||||
}
|
||||
cmd, err := List(config, extraArgs, inputPkgs)
|
||||
cmd, err := List(config, extraArgs, []string{inputPkg})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -164,6 +166,9 @@ func Load(config *compileopts.Config, inputPkgs []string, clangHeaders string, t
|
||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||
},
|
||||
}
|
||||
if addInstances != nil {
|
||||
addInstances(&pkg.info)
|
||||
}
|
||||
err := decoder.Decode(&pkg.PackageJSON)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package loader
|
||||
|
||||
// Workaround for Go 1.17 support. Should be removed once we drop Go 1.17
|
||||
// support.
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
addInstances = func(info *types.Info) {
|
||||
info.Instances = make(map[*ast.Ident]types.Instance)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
//
|
||||
// The program must already be parsed and type-checked with the .Parse() method.
|
||||
func (p *Program) LoadSSA() *ssa.Program {
|
||||
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug)
|
||||
prog := ssa.NewProgram(p.fset, ssa.SanityCheckFunctions|ssa.BareInits|ssa.GlobalDebug|ssa.InstantiateGenerics)
|
||||
|
||||
for _, pkg := range p.sorted {
|
||||
prog.CreatePackage(pkg.Pkg, pkg.Files, &pkg.info, true)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
@@ -1252,6 +1253,35 @@ func parseGoLinkFlag(flagsString string) (map[string]map[string]string, error) {
|
||||
return map[string]map[string]string(globalVarValues), nil
|
||||
}
|
||||
|
||||
// getListOfPackages returns a standard list of packages for a given list that might
|
||||
// include wildards using `go list`.
|
||||
// For example [./...] => ["pkg1", "pkg1/pkg12", "pkg2"]
|
||||
func getListOfPackages(pkgs []string, options *compileopts.Options) ([]string, error) {
|
||||
config, err := builder.NewConfig(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd, err := loader.List(config, nil, pkgs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to run `go list`: %w", err)
|
||||
}
|
||||
outputBuf := bytes.NewBuffer(nil)
|
||||
cmd.Stdout = outputBuf
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pkgNames []string
|
||||
sc := bufio.NewScanner(outputBuf)
|
||||
for sc.Scan() {
|
||||
pkgNames = append(pkgNames, sc.Text())
|
||||
}
|
||||
|
||||
return pkgNames, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Fprintln(os.Stderr, "No command-line arguments supplied.")
|
||||
@@ -1487,14 +1517,21 @@ func main() {
|
||||
if len(pkgNames) == 0 {
|
||||
pkgNames = []string{"."}
|
||||
}
|
||||
if outpath != "" && len(pkgNames) > 1 {
|
||||
|
||||
explicitPkgNames, err := getListOfPackages(pkgNames, options)
|
||||
if err != nil {
|
||||
fmt.Printf("cannot resolve packages: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if outpath != "" && len(explicitPkgNames) > 1 {
|
||||
fmt.Println("cannot use -o flag with multiple packages")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fail := make(chan struct{}, 1)
|
||||
var wg sync.WaitGroup
|
||||
bufs := make([]testOutputBuf, len(pkgNames))
|
||||
bufs := make([]testOutputBuf, len(explicitPkgNames))
|
||||
for i := range bufs {
|
||||
bufs[i].done = make(chan struct{})
|
||||
}
|
||||
@@ -1520,7 +1557,7 @@ func main() {
|
||||
// Build and run the tests concurrently.
|
||||
// This uses an additional semaphore to reduce the memory usage.
|
||||
testSema := make(chan struct{}, cap(options.Semaphore))
|
||||
for i, pkgName := range pkgNames {
|
||||
for i, pkgName := range explicitPkgNames {
|
||||
pkgName := pkgName
|
||||
buf := &bufs[i]
|
||||
testSema <- struct{}{}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -75,6 +76,7 @@ func TestBuild(t *testing.T) {
|
||||
tests = append(tests, "go1.17.go")
|
||||
}
|
||||
if minor >= 18 {
|
||||
tests = append(tests, "generics.go")
|
||||
tests = append(tests, "testing_go118.go")
|
||||
} else {
|
||||
tests = append(tests, "testing.go")
|
||||
@@ -520,6 +522,46 @@ func ioLogger(t *testing.T, wg *sync.WaitGroup) io.WriteCloser {
|
||||
return w
|
||||
}
|
||||
|
||||
func TestGetListOfPackages(t *testing.T) {
|
||||
opts := optionsFromTarget("", sema)
|
||||
tests := []struct {
|
||||
pkgs []string
|
||||
expectedPkgs []string
|
||||
expectesError bool
|
||||
}{
|
||||
{
|
||||
pkgs: []string{"./tests/testing/recurse/..."},
|
||||
expectedPkgs: []string{
|
||||
"github.com/tinygo-org/tinygo/tests/testing/recurse",
|
||||
"github.com/tinygo-org/tinygo/tests/testing/recurse/subdir",
|
||||
},
|
||||
},
|
||||
{
|
||||
pkgs: []string{"./tests/testing/pass"},
|
||||
expectedPkgs: []string{
|
||||
"github.com/tinygo-org/tinygo/tests/testing/pass",
|
||||
},
|
||||
},
|
||||
{
|
||||
pkgs: []string{"./tests/testing"},
|
||||
expectesError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
actualPkgs, err := getListOfPackages(test.pkgs, &opts)
|
||||
if err != nil && !test.expectesError {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
} else if err == nil && test.expectesError {
|
||||
t.Error("expected error, but got none")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.expectedPkgs, actualPkgs) {
|
||||
t.Errorf("expected two slices to be equal, expected %v got %v", test.expectedPkgs, actualPkgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This TestMain is necessary because TinyGo may also be invoked to run certain
|
||||
// LLVM tools in a separate process. Not capturing these invocations would lead
|
||||
// to recursive tests.
|
||||
|
||||
@@ -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`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// This is a echo console running on the os.Stdin and os.Stdout.
|
||||
// Stdin and os.Stdout are connected to machine.Serial in the baremetal target.
|
||||
//
|
||||
// Serial can be switched with the -serial option as follows
|
||||
// 1. tinygo flash -target wioterminal -serial usb examples/echo2
|
||||
// 2. tinygo flash -target wioterminal -serial uart examples/echo2
|
||||
//
|
||||
// This example will also work with standard Go.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Echo console enabled. Type something then press enter:\r\n")
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
||||
for {
|
||||
msg := ""
|
||||
fmt.Scanf("%s\n", &msg)
|
||||
fmt.Printf("You typed (scanf) : %s\r\n", msg)
|
||||
|
||||
if scanner.Scan() {
|
||||
fmt.Printf("You typed (scanner) : %s\r\n", scanner.Text())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb/hid/keyboard"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
button := machine.BUTTON
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
|
||||
kb := keyboard.New()
|
||||
|
||||
for {
|
||||
if !button.Get() {
|
||||
kb.Write([]byte("tinygo"))
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"machine/usb/hid/mouse"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
button := machine.BUTTON
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
|
||||
mouse := mouse.New()
|
||||
|
||||
for {
|
||||
if !button.Get() {
|
||||
for j := 0; j < 5; j++ {
|
||||
for i := 0; i < 100; i++ {
|
||||
mouse.Move(1, 0)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
mouse.Move(0, 1)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
mouse.Move(-1, -1)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"machine/usb/midi"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
button := machine.BUTTON
|
||||
button.Configure(machine.PinConfig{Mode: machine.PinInputPullup})
|
||||
|
||||
m := midi.New()
|
||||
m.SetCallback(func(b []byte) {
|
||||
led.Set(!led.Get())
|
||||
fmt.Printf("% X\r\n", b)
|
||||
m.Write(b)
|
||||
})
|
||||
|
||||
prev := true
|
||||
chords := []struct {
|
||||
name string
|
||||
keys []byte
|
||||
}{
|
||||
{name: "C ", keys: []byte{60, 64, 67}},
|
||||
{name: "G ", keys: []byte{55, 59, 62}},
|
||||
{name: "Am", keys: []byte{57, 60, 64}},
|
||||
{name: "F ", keys: []byte{53, 57, 60}},
|
||||
}
|
||||
index := 0
|
||||
|
||||
for {
|
||||
current := button.Get()
|
||||
if prev != current {
|
||||
led.Set(current)
|
||||
if current {
|
||||
for _, c := range chords[index].keys {
|
||||
m.Write([]byte{0x08, 0x80, c, 0x40})
|
||||
}
|
||||
index = (index + 1) % len(chords)
|
||||
} else {
|
||||
for _, c := range chords[index].keys {
|
||||
m.Write([]byte{0x09, 0x90, c, 0x40})
|
||||
}
|
||||
}
|
||||
prev = current
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -60,3 +60,14 @@ const (
|
||||
// Default Serial In Bus 1 for SPI communications
|
||||
SPI1_SDI_PIN = GPIO12 // Rx
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Adafruit Feather RP2040"
|
||||
usb_STRING_MANUFACTURER = "Adafruit"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x239A
|
||||
usb_PID uint16 = 0x80F1
|
||||
)
|
||||
|
||||
@@ -49,7 +49,7 @@ const (
|
||||
D36 = PA19 // ESP32 SPI SDO 1[3] PWM EXTI3
|
||||
D37 = NoPin // USB Host enable
|
||||
D38 = PA24 // USB DM
|
||||
D39 = PA27 // USB DP
|
||||
D39 = PA25 // USB DP
|
||||
D40 = PA03 // DAC/VREFP
|
||||
D41 = PB10 // Flash QSPI SCK
|
||||
D42 = PB11 // Flash QSPI CS
|
||||
|
||||
@@ -64,3 +64,14 @@ const (
|
||||
// Default Serial In Bus 1 for SPI communications
|
||||
SPI1_SDI_PIN = GPIO12 // Rx
|
||||
)
|
||||
|
||||
// USB CDC identifiers
|
||||
const (
|
||||
usb_STRING_PRODUCT = "Raspberry Pi Pico"
|
||||
usb_STRING_MANUFACTURER = "Raspberry Pi"
|
||||
)
|
||||
|
||||
var (
|
||||
usb_VID uint16 = 0x2E8A
|
||||
usb_PID uint16 = 0x0003
|
||||
)
|
||||
|
||||
@@ -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 |
|
||||
// #===========#===========#=============#===============#
|
||||
|
||||
@@ -355,20 +355,20 @@ var (
|
||||
|
||||
// I2C pins
|
||||
const (
|
||||
SDA0_PIN = PIN_WIRE_SDA // SDA: SERCOM3/PAD[0]
|
||||
SCL0_PIN = PIN_WIRE_SCL // SCL: SERCOM3/PAD[1]
|
||||
SDA1_PIN = PA17 // SDA: SERCOM3/PAD[0]
|
||||
SCL1_PIN = PA16 // SCL: SERCOM3/PAD[1]
|
||||
|
||||
SDA1_PIN = PIN_WIRE1_SDA // SDA: SERCOM4/PAD[0]
|
||||
SCL1_PIN = PIN_WIRE1_SCL // SCL: SERCOM4/PAD[1]
|
||||
SDA0_PIN = PA13 // SDA: SERCOM4/PAD[0]
|
||||
SCL0_PIN = PA12 // SCL: SERCOM4/PAD[1]
|
||||
|
||||
SDA_PIN = SDA0_PIN
|
||||
SCL_PIN = SCL0_PIN
|
||||
SDA_PIN = SDA1_PIN
|
||||
SCL_PIN = SCL1_PIN
|
||||
)
|
||||
|
||||
// I2C on the Wio Terminal
|
||||
var (
|
||||
I2C0 = sercomI2CM4
|
||||
I2C1 = sercomI2CM4
|
||||
I2C1 = sercomI2CM3
|
||||
)
|
||||
|
||||
// SPI pins
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
@@ -1735,889 +1734,6 @@ func (tcc *TCC) Set(channel uint8, value uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
|
||||
type USBCDC struct {
|
||||
Buffer *RingBuffer
|
||||
TxIdx volatile.Register8
|
||||
waitTxc bool
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
configured bool
|
||||
}
|
||||
|
||||
var (
|
||||
USB = &USBCDC{Buffer: NewRingBuffer()}
|
||||
)
|
||||
|
||||
const (
|
||||
usbcdcTxSizeMask uint8 = 0x3F
|
||||
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
|
||||
usbcdcTxBank1st uint8 = 0x00
|
||||
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
|
||||
usbcdcTxMaxRetriesAllowed uint8 = 5
|
||||
)
|
||||
|
||||
// Flush flushes buffered data.
|
||||
func (usbcdc *USBCDC) Flush() error {
|
||||
if usbLineInfo.lineState > 0 {
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
sz := idx & usbcdcTxSizeMask
|
||||
bk := idx & usbcdcTxBankMask
|
||||
if 0 < sz {
|
||||
|
||||
if usbcdc.waitTxc {
|
||||
// waiting for the next flush(), because the transmission is not complete
|
||||
usbcdc.waitTxcRetryCount++
|
||||
return nil
|
||||
}
|
||||
usbcdc.waitTxc = true
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
|
||||
// set the data
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk]))))
|
||||
if bk == usbcdcTxBank1st {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
|
||||
} else {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank1st)
|
||||
}
|
||||
|
||||
// clean multi packet size of bytes already sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set count of bytes to be sent
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
usbcdc.sent = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the USB CDC interface.
|
||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||
// Supposedly to handle problem with Windows USB serial ports?
|
||||
if usbLineInfo.lineState > 0 {
|
||||
ok := false
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
|
||||
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
|
||||
usbcdc.TxIdx.Set(idx + 1)
|
||||
ok = true
|
||||
}
|
||||
|
||||
interrupt.Restore(mask)
|
||||
|
||||
if ok {
|
||||
break
|
||||
} else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount {
|
||||
mask := interrupt.Disable()
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
usbcdc.TxIdx.Set(0)
|
||||
usbLineInfo.lineState = 0
|
||||
interrupt.Restore(mask)
|
||||
break
|
||||
} else {
|
||||
mask := interrupt.Disable()
|
||||
if usbcdc.sent {
|
||||
if usbcdc.waitTxc {
|
||||
if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) != 0 {
|
||||
setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.Flush()
|
||||
}
|
||||
} else {
|
||||
usbcdc.Flush()
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) DTR() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) RTS() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
||||
}
|
||||
|
||||
const (
|
||||
// these are SAMD21 specific.
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
|
||||
|
||||
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
|
||||
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
|
||||
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||
)
|
||||
|
||||
var (
|
||||
usbEndpointDescriptors [8]usbDeviceDescriptor
|
||||
|
||||
udd_ep_in_cache_buffer [7][128]uint8
|
||||
udd_ep_out_cache_buffer [7][128]uint8
|
||||
|
||||
isEndpointHalt = false
|
||||
isRemoteWakeUpEnabled = false
|
||||
endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
|
||||
(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointIn)}
|
||||
|
||||
usbConfiguration uint8
|
||||
usbSetInterface uint8
|
||||
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
|
||||
)
|
||||
|
||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
||||
func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||
// reset USB interface
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
|
||||
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
|
||||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
|
||||
|
||||
// configure pins
|
||||
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
|
||||
// performs pad calibration from store fuses
|
||||
handlePadCalibration()
|
||||
|
||||
// run in standby
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
|
||||
|
||||
// set full speed
|
||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
|
||||
// enable interrupt for start of frame
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
|
||||
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
intr := interrupt.New(sam.IRQ_USB, handleUSB)
|
||||
intr.Enable()
|
||||
|
||||
usbcdc.configured = true
|
||||
}
|
||||
|
||||
// Configured returns whether usbcdc is configured or not.
|
||||
func (usbcdc *USBCDC) Configured() bool {
|
||||
return usbcdc.configured
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// This requires registers that are not included in the SVD file.
|
||||
// Modeled after defines from samd21g18a.h and nvmctrl.h:
|
||||
//
|
||||
// #define NVMCTRL_OTP4 0x00806020
|
||||
//
|
||||
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
|
||||
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
|
||||
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
|
||||
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
|
||||
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
|
||||
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
|
||||
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
|
||||
//
|
||||
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
|
||||
calibTransN := uint16(fuse>>13) & uint16(0x1f)
|
||||
calibTransP := uint16(fuse>>18) & uint16(0x1f)
|
||||
calibTrim := uint16(fuse>>23) & uint16(0x7)
|
||||
|
||||
if calibTransN == 0x1f {
|
||||
calibTransN = 5
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
|
||||
|
||||
if calibTransP == 0x1f {
|
||||
calibTransP = 29
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||
|
||||
if calibTrim == 0x7 {
|
||||
calibTrim = 3
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
func handleUSB(intr interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
// End of reset
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
|
||||
// Configure control endpoint
|
||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
||||
|
||||
// Enable Setup-Received interrupt
|
||||
setEPINTENSET(0, sam.USB_DEVICE_EPINTENSET_RXSTP)
|
||||
|
||||
usbConfiguration = 0
|
||||
|
||||
// ack the End-Of-Reset interrupt
|
||||
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
|
||||
}
|
||||
|
||||
// Start of frame
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||
USB.Flush()
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
// Endpoint 0 Setup interrupt
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_RXSTP > 0 {
|
||||
// ack setup received
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_RXSTP)
|
||||
|
||||
// parse setup
|
||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
// Clear the Bank 0 ready flag on Control OUT
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
ok := false
|
||||
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.wIndex == usb_CDC_ACM_INTERFACE {
|
||||
ok = cdcSetup(setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_STALL1 > 0 {
|
||||
// ack the stall
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_STALL1)
|
||||
|
||||
// clear stall request
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_EPINTENCLR_STALL1)
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
if (epFlags&sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 ||
|
||||
(epFlags&sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 {
|
||||
switch i {
|
||||
case usb_CDC_ENDPOINT_OUT:
|
||||
handleEndpoint(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM:
|
||||
setEPSTATUSCLR(i, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
if i == usb_CDC_ENDPOINT_IN {
|
||||
USB.waitTxc = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// ready for next transfer
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// Control OUT
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// Control IN
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// Prepare OUT endpoint for receive
|
||||
// set multi packet size for expected number of receive bytes on control OUT
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// NAK on endpoint OUT to show we are ready to receive control data
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_BK0RDY)
|
||||
}
|
||||
}
|
||||
|
||||
func handleStandardSetup(setup usbSetup) bool {
|
||||
switch setup.bRequest {
|
||||
case usb_GET_STATUS:
|
||||
buf := []byte{0, 0}
|
||||
|
||||
if setup.bmRequestType != 0 { // endpoint
|
||||
// TODO: actually check if the endpoint in question is currently halted
|
||||
if isEndpointHalt {
|
||||
buf[0] = 1
|
||||
}
|
||||
}
|
||||
|
||||
sendUSBPacket(0, buf)
|
||||
return true
|
||||
|
||||
case usb_CLEAR_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = false
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = false
|
||||
}
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
case usb_SET_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = true
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = true
|
||||
}
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
case usb_SET_ADDRESS:
|
||||
// set packet size 64 with auto Zlp after transfer
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
|
||||
uint32(1<<31)) // autozlp
|
||||
|
||||
// ack the transfer is complete from the request
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
// set bank ready for data
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
sam.USB_DEVICE.DADD.SetBits(setup.wValueL)
|
||||
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
|
||||
|
||||
return true
|
||||
|
||||
case usb_GET_DESCRIPTOR:
|
||||
sendDescriptor(setup)
|
||||
return true
|
||||
|
||||
case usb_SET_DESCRIPTOR:
|
||||
return false
|
||||
|
||||
case usb_GET_CONFIGURATION:
|
||||
buff := []byte{usbConfiguration}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_CONFIGURATION:
|
||||
if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE {
|
||||
for i := 1; i < len(endPoints); i++ {
|
||||
initEndpoint(uint32(i), endPoints[i])
|
||||
}
|
||||
|
||||
usbConfiguration = setup.wValueL
|
||||
|
||||
// Enable interrupt for CDC control messages from host (OUT packet)
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_EPINTENSET_TRCPT1)
|
||||
|
||||
// Enable interrupt for CDC data messages from host
|
||||
setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||
|
||||
sendZlp()
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
case usb_GET_INTERFACE:
|
||||
buff := []byte{usbSetInterface}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_INTERFACE:
|
||||
usbSetInterface = setup.wValueL
|
||||
|
||||
sendZlp()
|
||||
return true
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
b, err := receiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.wValueL
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING || setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
// auto-reset into the bootloader
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
ResetProcessor()
|
||||
} else {
|
||||
// TODO: cancel any reset
|
||||
}
|
||||
sendZlp()
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SEND_BREAK {
|
||||
// TODO: something with this value?
|
||||
// breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
|
||||
// return false;
|
||||
sendZlp()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data)
|
||||
|
||||
// Set endpoint address for sending data
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// clear multi-packet size which is total bytes already sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count, which is total number of bytes to be sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(uint32((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos))
|
||||
}
|
||||
|
||||
func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until OUT transfer is completed.
|
||||
timeout = 300000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// get data
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpoint(ep uint32) {
|
||||
// get data
|
||||
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
// move to ring buffer
|
||||
for i := 0; i < count; i++ {
|
||||
USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF)))
|
||||
}
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set multi packet size to 64
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
}
|
||||
|
||||
func sendZlp() {
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func epPacketSize(size uint16) uint32 {
|
||||
switch size {
|
||||
case 8:
|
||||
return 0
|
||||
case 16:
|
||||
return 1
|
||||
case 32:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
case 128:
|
||||
return 4
|
||||
case 256:
|
||||
return 5
|
||||
case 512:
|
||||
return 6
|
||||
case 1023:
|
||||
return 7
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPCFG(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPCFG0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPCFG1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPCFG2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPCFG3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPCFG4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPCFG5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPCFG6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPCFG7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func setEPCFG(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPCFG0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPCFG1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPCFG2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPCFG3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPCFG4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPCFG5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPCFG6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPCFG7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPSTATUSCLR0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPSTATUSCLR1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPSTATUSCLR2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPSTATUSCLR3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPSTATUSCLR4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPSTATUSCLR5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPSTATUSCLR6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPSTATUSCLR7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPSTATUSSET0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPSTATUSSET1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPSTATUSSET2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPSTATUSSET3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPSTATUSSET4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPSTATUSSET5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPSTATUSSET6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPSTATUSSET7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getEPSTATUS(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPSTATUS0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPSTATUS1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPSTATUS2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPSTATUS3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPSTATUS4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPSTATUS5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPSTATUS6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPSTATUS7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPINTFLAG(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPINTFLAG0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPINTFLAG1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPINTFLAG2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPINTFLAG3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPINTFLAG4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPINTFLAG5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPINTFLAG6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPINTFLAG7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTFLAG(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTFLAG0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTFLAG1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTFLAG2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTFLAG3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTFLAG4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTFLAG5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTFLAG6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTFLAG7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTENCLR(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTENCLR0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTENCLR1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTENCLR2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTENCLR3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTENCLR4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTENCLR5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTENCLR6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTENCLR7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTENSET0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTENSET1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTENSET2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTENSET3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTENSET4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTENSET5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTENSET6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTENSET7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ResetProcessor should perform a system reset in preperation
|
||||
// to switch to the bootloader to flash new firmware.
|
||||
func ResetProcessor() {
|
||||
|
||||
@@ -0,0 +1,628 @@
|
||||
//go:build sam && atsamd21
|
||||
// +build sam,atsamd21
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// these are SAMD21 specific.
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
|
||||
|
||||
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
|
||||
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
|
||||
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||
)
|
||||
|
||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
// reset USB interface
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
|
||||
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
|
||||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
|
||||
|
||||
// configure pins
|
||||
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
|
||||
// performs pad calibration from store fuses
|
||||
handlePadCalibration()
|
||||
|
||||
// run in standby
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
|
||||
|
||||
// set full speed
|
||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
|
||||
// enable interrupt for start of frame
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
|
||||
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
interrupt.New(sam.IRQ_USB, handleUSBIRQ).Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// This requires registers that are not included in the SVD file.
|
||||
// Modeled after defines from samd21g18a.h and nvmctrl.h:
|
||||
//
|
||||
// #define NVMCTRL_OTP4 0x00806020
|
||||
//
|
||||
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
|
||||
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
|
||||
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
|
||||
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
|
||||
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
|
||||
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
|
||||
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
|
||||
//
|
||||
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
|
||||
calibTransN := uint16(fuse>>13) & uint16(0x1f)
|
||||
calibTransP := uint16(fuse>>18) & uint16(0x1f)
|
||||
calibTrim := uint16(fuse>>23) & uint16(0x7)
|
||||
|
||||
if calibTransN == 0x1f {
|
||||
calibTransN = 5
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
|
||||
|
||||
if calibTransP == 0x1f {
|
||||
calibTransP = 29
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||
|
||||
if calibTrim == 0x7 {
|
||||
calibTrim = 3
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
// End of reset
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
|
||||
// Configure control endpoint
|
||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
||||
|
||||
usbConfiguration = 0
|
||||
|
||||
// ack the End-Of-Reset interrupt
|
||||
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
|
||||
}
|
||||
|
||||
// Start of frame
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
// Endpoint 0 Setup interrupt
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_RXSTP > 0 {
|
||||
// ack setup received
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_RXSTP)
|
||||
|
||||
// parse setup
|
||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
// Clear the Bank 0 ready flag on Control OUT
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
ok := false
|
||||
if (setup.BmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil {
|
||||
ok = callbackUSBSetup[setup.WIndex](setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_STALL1 > 0 {
|
||||
// ack the stall
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_STALL1)
|
||||
|
||||
// clear stall request
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_EPINTENCLR_STALL1)
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 {
|
||||
buf := handleEndpointRx(i)
|
||||
if callbackUSBRx[i] != nil {
|
||||
callbackUSBRx[i](buf)
|
||||
}
|
||||
} else if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 {
|
||||
if callbackUSBTx[i] != nil {
|
||||
callbackUSBTx[i]()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT1)
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// ready for next transfer
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY)
|
||||
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT1)
|
||||
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// Control OUT
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// Control IN
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// Prepare OUT endpoint for receive
|
||||
// set multi packet size for expected number of receive bytes on control OUT
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// NAK on endpoint OUT to show we are ready to receive control data
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_BK0RDY)
|
||||
|
||||
// Enable Setup-Received interrupt
|
||||
setEPINTENSET(0, sam.USB_DEVICE_EPINTENSET_RXSTP)
|
||||
}
|
||||
}
|
||||
|
||||
func handleUSBSetAddress(setup USBSetup) bool {
|
||||
// set packet size 64 with auto Zlp after transfer
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
|
||||
uint32(1<<31)) // autozlp
|
||||
|
||||
// ack the transfer is complete from the request
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
// set bank ready for data
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
sam.USB_DEVICE.DADD.SetBits(setup.WValueL)
|
||||
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data, 0)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(ep, sam.USB_DEVICE_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_BK1RDY)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
l := uint16(len(data))
|
||||
if 0 < maxsize && maxsize < l {
|
||||
l = maxsize
|
||||
}
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
|
||||
|
||||
// Set endpoint address for sending data
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// clear multi-packet size which is total bytes already sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count, which is total number of bytes to be sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until OUT transfer is completed.
|
||||
timeout = 300000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// get data
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpointRx(ep uint32) []byte {
|
||||
// get data
|
||||
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
// move to ring buffer
|
||||
buf := make([]byte, count)
|
||||
copy(buf, udd_ep_out_cache_buffer[ep][:])
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set multi packet size to 64
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
return buf[:count]
|
||||
}
|
||||
|
||||
func SendZlp() {
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func epPacketSize(size uint16) uint32 {
|
||||
switch size {
|
||||
case 8:
|
||||
return 0
|
||||
case 16:
|
||||
return 1
|
||||
case 32:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
case 128:
|
||||
return 4
|
||||
case 256:
|
||||
return 5
|
||||
case 512:
|
||||
return 6
|
||||
case 1023:
|
||||
return 7
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPCFG(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPCFG0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPCFG1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPCFG2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPCFG3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPCFG4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPCFG5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPCFG6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPCFG7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func setEPCFG(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPCFG0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPCFG1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPCFG2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPCFG3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPCFG4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPCFG5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPCFG6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPCFG7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPSTATUSCLR0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPSTATUSCLR1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPSTATUSCLR2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPSTATUSCLR3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPSTATUSCLR4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPSTATUSCLR5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPSTATUSCLR6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPSTATUSCLR7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPSTATUSSET0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPSTATUSSET1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPSTATUSSET2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPSTATUSSET3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPSTATUSSET4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPSTATUSSET5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPSTATUSSET6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPSTATUSSET7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getEPSTATUS(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPSTATUS0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPSTATUS1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPSTATUS2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPSTATUS3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPSTATUS4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPSTATUS5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPSTATUS6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPSTATUS7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPINTFLAG(ep uint32) uint8 {
|
||||
switch ep {
|
||||
case 0:
|
||||
return sam.USB_DEVICE.EPINTFLAG0.Get()
|
||||
case 1:
|
||||
return sam.USB_DEVICE.EPINTFLAG1.Get()
|
||||
case 2:
|
||||
return sam.USB_DEVICE.EPINTFLAG2.Get()
|
||||
case 3:
|
||||
return sam.USB_DEVICE.EPINTFLAG3.Get()
|
||||
case 4:
|
||||
return sam.USB_DEVICE.EPINTFLAG4.Get()
|
||||
case 5:
|
||||
return sam.USB_DEVICE.EPINTFLAG5.Get()
|
||||
case 6:
|
||||
return sam.USB_DEVICE.EPINTFLAG6.Get()
|
||||
case 7:
|
||||
return sam.USB_DEVICE.EPINTFLAG7.Get()
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTFLAG(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTFLAG0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTFLAG1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTFLAG2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTFLAG3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTFLAG4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTFLAG5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTFLAG6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTFLAG7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTENCLR(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTENCLR0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTENCLR1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTENCLR2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTENCLR3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTENCLR4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTENCLR5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTENCLR6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTENCLR7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
switch ep {
|
||||
case 0:
|
||||
sam.USB_DEVICE.EPINTENSET0.Set(val)
|
||||
case 1:
|
||||
sam.USB_DEVICE.EPINTENSET1.Set(val)
|
||||
case 2:
|
||||
sam.USB_DEVICE.EPINTENSET2.Set(val)
|
||||
case 3:
|
||||
sam.USB_DEVICE.EPINTENSET3.Set(val)
|
||||
case 4:
|
||||
sam.USB_DEVICE.EPINTENSET4.Set(val)
|
||||
case 5:
|
||||
sam.USB_DEVICE.EPINTENSET5.Set(val)
|
||||
case 6:
|
||||
sam.USB_DEVICE.EPINTENSET6.Set(val)
|
||||
case 7:
|
||||
sam.USB_DEVICE.EPINTENSET7.Set(val)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -0,0 +1,460 @@
|
||||
//go:build (sam && atsamd51) || (sam && atsame5x)
|
||||
// +build sam,atsamd51 sam,atsame5x
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// these are SAMD51 specific.
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF
|
||||
|
||||
usb_DEVICE_PCKSIZE_SIZE_Pos = 28
|
||||
usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7
|
||||
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14
|
||||
usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF
|
||||
)
|
||||
|
||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
// reset USB interface
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST)
|
||||
for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) ||
|
||||
sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) {
|
||||
}
|
||||
|
||||
sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors))))
|
||||
|
||||
// configure pins
|
||||
USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom})
|
||||
|
||||
// performs pad calibration from store fuses
|
||||
handlePadCalibration()
|
||||
|
||||
// run in standby
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY)
|
||||
|
||||
// set full speed
|
||||
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos)
|
||||
|
||||
// attach
|
||||
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
|
||||
|
||||
// enable interrupt for end of reset
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST)
|
||||
|
||||
// enable interrupt for start of frame
|
||||
sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF)
|
||||
|
||||
// enable USB
|
||||
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
|
||||
|
||||
// enable IRQ
|
||||
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
|
||||
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
|
||||
}
|
||||
|
||||
func handlePadCalibration() {
|
||||
// Load Pad Calibration data from non-volatile memory
|
||||
// This requires registers that are not included in the SVD file.
|
||||
// Modeled after defines from samd21g18a.h and nvmctrl.h:
|
||||
//
|
||||
// #define NVMCTRL_OTP4 0x00806020
|
||||
//
|
||||
// #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */
|
||||
// #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos)
|
||||
// #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */
|
||||
// #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos)
|
||||
// #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos)))
|
||||
|
||||
// #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4)
|
||||
// #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */
|
||||
// #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos)
|
||||
// #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos)))
|
||||
//
|
||||
fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4))
|
||||
calibTransN := uint16(fuse>>13) & uint16(0x1f)
|
||||
calibTransP := uint16(fuse>>18) & uint16(0x1f)
|
||||
calibTrim := uint16(fuse>>23) & uint16(0x7)
|
||||
|
||||
if calibTransN == 0x1f {
|
||||
calibTransN = 5
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos)
|
||||
|
||||
if calibTransP == 0x1f {
|
||||
calibTransP = 29
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos)
|
||||
|
||||
if calibTrim == 0x7 {
|
||||
calibTrim = 3
|
||||
}
|
||||
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
|
||||
}
|
||||
|
||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
// reset all interrupt flags
|
||||
flags := sam.USB_DEVICE.INTFLAG.Get()
|
||||
sam.USB_DEVICE.INTFLAG.Set(flags)
|
||||
|
||||
// End of reset
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 {
|
||||
// Configure control endpoint
|
||||
initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL)
|
||||
|
||||
usbConfiguration = 0
|
||||
|
||||
// ack the End-Of-Reset interrupt
|
||||
sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST)
|
||||
}
|
||||
|
||||
// Start of frame
|
||||
if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 {
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
// Endpoint 0 Setup interrupt
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP > 0 {
|
||||
// ack setup received
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP)
|
||||
|
||||
// parse setup
|
||||
setup := newUSBSetup(udd_ep_out_cache_buffer[0][:])
|
||||
|
||||
// Clear the Bank 0 ready flag on Control OUT
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
ok := false
|
||||
if (setup.BmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil {
|
||||
ok = callbackUSBSetup[setup.WIndex](setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 {
|
||||
// ack the stall
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
|
||||
// clear stall request
|
||||
setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1)
|
||||
}
|
||||
}
|
||||
|
||||
// Now the actual transfer handlers, ignore endpoint number 0 (setup)
|
||||
var i uint32
|
||||
for i = 1; i < uint32(len(endPoints)); i++ {
|
||||
// Check if endpoint has a pending interrupt
|
||||
epFlags := getEPINTFLAG(i)
|
||||
setEPINTFLAG(i, epFlags)
|
||||
if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 {
|
||||
buf := handleEndpointRx(i)
|
||||
if callbackUSBRx[i] != nil {
|
||||
callbackUSBRx[i](buf)
|
||||
}
|
||||
} else if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 {
|
||||
if callbackUSBTx[i] != nil {
|
||||
callbackUSBTx[i]()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// receive interrupts when current transfer complete
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// ready for next transfer
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// NAK on endpoint IN, the bank is not yet filled in.
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY)
|
||||
|
||||
setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
||||
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// Control OUT
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos))
|
||||
|
||||
// Control IN
|
||||
// set packet size
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
// set data buffer address
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// set endpoint type
|
||||
setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos))
|
||||
|
||||
// Prepare OUT endpoint for receive
|
||||
// set multi packet size for expected number of receive bytes on control OUT
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count to zero, we have not received anything yet
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// NAK on endpoint OUT to show we are ready to receive control data
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK0RDY)
|
||||
|
||||
// Enable Setup-Received interrupt
|
||||
setEPINTENSET(0, sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP)
|
||||
}
|
||||
}
|
||||
|
||||
func handleUSBSetAddress(setup USBSetup) bool {
|
||||
// set packet size 64 with auto Zlp after transfer
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.Set((epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) |
|
||||
uint32(1<<31)) // autozlp
|
||||
|
||||
// ack the transfer is complete from the request
|
||||
setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// set bank ready for data
|
||||
setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
sam.USB_DEVICE.DADD.SetBits(setup.WValueL)
|
||||
sam.USB_DEVICE.DADD.SetBits(sam.USB_DEVICE_DADD_ADDEN)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data, 0)
|
||||
|
||||
// clear transfer complete flag
|
||||
setEPINTFLAG(ep, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
|
||||
// send data by setting bank ready
|
||||
setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
l := uint16(len(data))
|
||||
if 0 < maxsize && maxsize < l {
|
||||
l = maxsize
|
||||
}
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data[:l])
|
||||
|
||||
// Set endpoint address for sending data
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))))
|
||||
|
||||
// clear multi-packet size which is total bytes already sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set byte count, which is total number of bytes to be sent
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
// address
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
// Wait until OUT transfer is ready.
|
||||
timeout := 300000
|
||||
for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until OUT transfer is completed.
|
||||
timeout = 300000
|
||||
for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// get data
|
||||
bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
if bytesread != cdcLineInfoSize {
|
||||
return b, errUSBCDCBytesRead
|
||||
}
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:7])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpointRx(ep uint32) []byte {
|
||||
// get data
|
||||
count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >>
|
||||
usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)
|
||||
|
||||
// move to ring buffer
|
||||
buf := make([]byte, count)
|
||||
copy(buf, udd_ep_out_cache_buffer[ep][:])
|
||||
|
||||
// set byte count to zero
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
|
||||
// set multi packet size to 64
|
||||
usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos)
|
||||
|
||||
// set ready for next data
|
||||
setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY)
|
||||
|
||||
return buf[:count]
|
||||
}
|
||||
|
||||
func SendZlp() {
|
||||
usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos)
|
||||
}
|
||||
|
||||
func epPacketSize(size uint16) uint32 {
|
||||
switch size {
|
||||
case 8:
|
||||
return 0
|
||||
case 16:
|
||||
return 1
|
||||
case 32:
|
||||
return 2
|
||||
case 64:
|
||||
return 3
|
||||
case 128:
|
||||
return 4
|
||||
case 256:
|
||||
return 5
|
||||
case 512:
|
||||
return 6
|
||||
case 1023:
|
||||
return 7
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func getEPCFG(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Get()
|
||||
}
|
||||
|
||||
func setEPCFG(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSCLR(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPSTATUSSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSSET.Set(val)
|
||||
}
|
||||
|
||||
func getEPSTATUS(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUS.Get()
|
||||
}
|
||||
|
||||
func getEPINTFLAG(ep uint32) uint8 {
|
||||
return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Get()
|
||||
}
|
||||
|
||||
func setEPINTFLAG(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENCLR(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENCLR.Set(val)
|
||||
}
|
||||
|
||||
func setEPINTENSET(ep uint32, val uint8) {
|
||||
sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val)
|
||||
}
|
||||
+121
-321
@@ -11,142 +11,15 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// USBCDC is the USB CDC aka serial over USB interface on the nRF52840
|
||||
type USBCDC struct {
|
||||
Buffer *RingBuffer
|
||||
interrupt interrupt.Interrupt
|
||||
initcomplete bool
|
||||
TxIdx volatile.Register8
|
||||
waitTxc bool
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
}
|
||||
|
||||
const (
|
||||
usbcdcTxSizeMask uint8 = 0x3F
|
||||
usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask
|
||||
usbcdcTxBank1st uint8 = 0x00
|
||||
usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1
|
||||
usbcdcTxMaxRetriesAllowed uint8 = 5
|
||||
)
|
||||
|
||||
// Flush flushes buffered data.
|
||||
func (usbcdc *USBCDC) Flush() error {
|
||||
if usbLineInfo.lineState > 0 {
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
sz := idx & usbcdcTxSizeMask
|
||||
bk := idx & usbcdcTxBankMask
|
||||
if 0 < sz {
|
||||
|
||||
if usbcdc.waitTxc {
|
||||
// waiting for the next flush(), because the transmission is not complete
|
||||
usbcdc.waitTxcRetryCount++
|
||||
return nil
|
||||
}
|
||||
usbcdc.waitTxc = true
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
|
||||
// set the data
|
||||
enterCriticalSection()
|
||||
sendViaEPIn(
|
||||
usb_CDC_ENDPOINT_IN,
|
||||
&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk],
|
||||
int(sz),
|
||||
)
|
||||
if bk == usbcdcTxBank1st {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank2nd)
|
||||
} else {
|
||||
usbcdc.TxIdx.Set(usbcdcTxBank1st)
|
||||
}
|
||||
|
||||
usbcdc.sent = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the USB CDC interface.
|
||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||
// Supposedly to handle problem with Windows USB serial ports?
|
||||
if usbLineInfo.lineState > 0 {
|
||||
ok := false
|
||||
for {
|
||||
mask := interrupt.Disable()
|
||||
|
||||
idx := usbcdc.TxIdx.Get()
|
||||
if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask {
|
||||
udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c
|
||||
usbcdc.TxIdx.Set(idx + 1)
|
||||
ok = true
|
||||
}
|
||||
|
||||
interrupt.Restore(mask)
|
||||
|
||||
if ok {
|
||||
break
|
||||
} else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount {
|
||||
mask := interrupt.Disable()
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.waitTxcRetryCount = 0
|
||||
usbcdc.TxIdx.Set(0)
|
||||
usbLineInfo.lineState = 0
|
||||
interrupt.Restore(mask)
|
||||
break
|
||||
} else {
|
||||
mask := interrupt.Disable()
|
||||
if usbcdc.sent {
|
||||
if usbcdc.waitTxc {
|
||||
if !easyDMABusy.HasBits(1) {
|
||||
usbcdc.waitTxc = false
|
||||
usbcdc.Flush()
|
||||
}
|
||||
} else {
|
||||
usbcdc.Flush()
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) DTR() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) RTS() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
||||
}
|
||||
|
||||
var (
|
||||
USB = &_USB
|
||||
_USB = USBCDC{Buffer: NewRingBuffer()}
|
||||
|
||||
usbEndpointDescriptors [8]usbDeviceDescriptor
|
||||
|
||||
udd_ep_in_cache_buffer [7][128]uint8
|
||||
udd_ep_out_cache_buffer [7][128]uint8
|
||||
|
||||
sendOnEP0DATADONE struct {
|
||||
ptr *byte
|
||||
count int
|
||||
ptr *byte
|
||||
count int
|
||||
offset int
|
||||
}
|
||||
isEndpointHalt = false
|
||||
isRemoteWakeUpEnabled = false
|
||||
endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
|
||||
(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
|
||||
(usb_ENDPOINT_TYPE_BULK | usbEndpointIn)}
|
||||
|
||||
usbConfiguration uint8
|
||||
usbSetInterface uint8
|
||||
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
|
||||
epinen uint32
|
||||
epouten uint32
|
||||
easyDMABusy volatile.Register8
|
||||
epout0data_setlinecoding bool
|
||||
epinen uint32
|
||||
epouten uint32
|
||||
easyDMABusy volatile.Register8
|
||||
)
|
||||
|
||||
// enterCriticalSection is used to protect access to easyDMA - only one thing
|
||||
@@ -166,19 +39,15 @@ func exitCriticalSection() {
|
||||
easyDMABusy.ClearBits(1)
|
||||
}
|
||||
|
||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
||||
func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||
if usbcdc.initcomplete {
|
||||
return
|
||||
}
|
||||
|
||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
// Enable IRQ. Make sure this is higher than the SWI2 interrupt handler so
|
||||
// that it is possible to print to the console from a BLE interrupt. You
|
||||
// shouldn't generally do that but it is useful for debugging and panic
|
||||
// logging.
|
||||
usbcdc.interrupt = interrupt.New(nrf.IRQ_USBD, _USB.handleInterrupt)
|
||||
usbcdc.interrupt.SetPriority(0x40) // interrupt priority 2 (lower number means more important)
|
||||
usbcdc.interrupt.Enable()
|
||||
intr := interrupt.New(nrf.IRQ_USBD, handleUSBIRQ)
|
||||
intr.SetPriority(0x40) // interrupt priority 2 (lower number means more important)
|
||||
intr.Enable()
|
||||
|
||||
// enable USB
|
||||
nrf.USBD.ENABLE.Set(1)
|
||||
@@ -193,14 +62,12 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) {
|
||||
)
|
||||
|
||||
nrf.USBD.USBPULLUP.Set(0)
|
||||
|
||||
usbcdc.initcomplete = true
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
if nrf.USBD.EVENTS_SOF.Get() == 1 {
|
||||
nrf.USBD.EVENTS_SOF.Set(0)
|
||||
usbcdc.Flush()
|
||||
|
||||
// if you want to blink LED showing traffic, this would be the place...
|
||||
}
|
||||
|
||||
@@ -224,25 +91,30 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
||||
if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 {
|
||||
// done sending packet - either need to send another or enter status stage
|
||||
nrf.USBD.EVENTS_EP0DATADONE.Set(0)
|
||||
if epout0data_setlinecoding {
|
||||
nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
nrf.USBD.EPOUT[0].MAXCNT.Set(64)
|
||||
nrf.USBD.TASKS_STARTEPOUT[0].Set(1)
|
||||
return
|
||||
}
|
||||
if sendOnEP0DATADONE.ptr != nil {
|
||||
// previous data was too big for one packet, so send a second
|
||||
ptr := sendOnEP0DATADONE.ptr
|
||||
count := sendOnEP0DATADONE.count
|
||||
if count > usbEndpointPacketSize {
|
||||
sendOnEP0DATADONE.offset += usbEndpointPacketSize
|
||||
sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[0][sendOnEP0DATADONE.offset]
|
||||
count = usbEndpointPacketSize
|
||||
}
|
||||
sendOnEP0DATADONE.count -= count
|
||||
sendViaEPIn(
|
||||
0,
|
||||
sendOnEP0DATADONE.ptr,
|
||||
sendOnEP0DATADONE.count,
|
||||
ptr,
|
||||
count,
|
||||
)
|
||||
|
||||
// clear, so we know we're done
|
||||
sendOnEP0DATADONE.ptr = nil
|
||||
if sendOnEP0DATADONE.count == 0 {
|
||||
sendOnEP0DATADONE.ptr = nil
|
||||
sendOnEP0DATADONE.offset = 0
|
||||
}
|
||||
} else {
|
||||
// no more data, so set status stage
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
SendZlp() // nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -256,12 +128,13 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
||||
setup := parseUSBSetupRegisters()
|
||||
|
||||
ok := false
|
||||
if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
if (setup.BmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
if setup.wIndex == usb_CDC_ACM_INTERFACE {
|
||||
ok = cdcSetup(setup)
|
||||
// Class Interface Requests
|
||||
if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil {
|
||||
ok = callbackUSBSetup[setup.WIndex](setup)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,23 +154,16 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
||||
// Check if endpoint has a pending interrupt
|
||||
inDataDone := epDataStatus&(nrf.USBD_EPDATASTATUS_EPIN1<<(i-1)) > 0
|
||||
outDataDone := epDataStatus&(nrf.USBD_EPDATASTATUS_EPOUT1<<(i-1)) > 0
|
||||
if inDataDone || outDataDone {
|
||||
switch i {
|
||||
case usb_CDC_ENDPOINT_OUT:
|
||||
// setup buffer to receive from host
|
||||
if outDataDone {
|
||||
enterCriticalSection()
|
||||
nrf.USBD.EPOUT[i].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[i]))))
|
||||
count := nrf.USBD.SIZE.EPOUT[i].Get()
|
||||
nrf.USBD.EPOUT[i].MAXCNT.Set(count)
|
||||
nrf.USBD.TASKS_STARTEPOUT[i].Set(1)
|
||||
}
|
||||
case usb_CDC_ENDPOINT_IN: //, usb_CDC_ENDPOINT_ACM:
|
||||
if inDataDone {
|
||||
usbcdc.waitTxc = false
|
||||
exitCriticalSection()
|
||||
}
|
||||
if inDataDone {
|
||||
if callbackUSBTx[i] != nil {
|
||||
callbackUSBTx[i]()
|
||||
}
|
||||
} else if outDataDone {
|
||||
enterCriticalSection()
|
||||
nrf.USBD.EPOUT[i].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[i]))))
|
||||
count := nrf.USBD.SIZE.EPOUT[i].Get()
|
||||
nrf.USBD.EPOUT[i].MAXCNT.Set(count)
|
||||
nrf.USBD.TASKS_STARTEPOUT[i].Set(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,38 +172,23 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
|
||||
for i := 0; i < len(endPoints); i++ {
|
||||
if nrf.USBD.EVENTS_ENDEPOUT[i].Get() > 0 {
|
||||
nrf.USBD.EVENTS_ENDEPOUT[i].Set(0)
|
||||
if i == 0 && epout0data_setlinecoding {
|
||||
epout0data_setlinecoding = false
|
||||
count := int(nrf.USBD.SIZE.EPOUT[0].Get())
|
||||
if count >= 7 {
|
||||
parseUSBLineInfo(udd_ep_out_cache_buffer[0][:count])
|
||||
checkShouldReset()
|
||||
}
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
if i == usb_CDC_ENDPOINT_OUT {
|
||||
usbcdc.handleEndpoint(uint32(i))
|
||||
buf := handleEndpointRx(uint32(i))
|
||||
if callbackUSBRx[i] != nil {
|
||||
callbackUSBRx[i](buf)
|
||||
}
|
||||
exitCriticalSection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseUSBLineInfo(b []byte) {
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
func parseUSBSetupRegisters() usbSetup {
|
||||
return usbSetup{
|
||||
bmRequestType: uint8(nrf.USBD.BMREQUESTTYPE.Get()),
|
||||
bRequest: uint8(nrf.USBD.BREQUEST.Get()),
|
||||
wValueL: uint8(nrf.USBD.WVALUEL.Get()),
|
||||
wValueH: uint8(nrf.USBD.WVALUEH.Get()),
|
||||
wIndex: uint16((nrf.USBD.WINDEXH.Get() << 8) | nrf.USBD.WINDEXL.Get()),
|
||||
wLength: uint16(((nrf.USBD.WLENGTHH.Get() & 0xff) << 8) | (nrf.USBD.WLENGTHL.Get() & 0xff)),
|
||||
func parseUSBSetupRegisters() USBSetup {
|
||||
return USBSetup{
|
||||
BmRequestType: uint8(nrf.USBD.BMREQUESTTYPE.Get()),
|
||||
BRequest: uint8(nrf.USBD.BREQUEST.Get()),
|
||||
WValueL: uint8(nrf.USBD.WVALUEL.Get()),
|
||||
WValueH: uint8(nrf.USBD.WVALUEH.Get()),
|
||||
WIndex: uint16((nrf.USBD.WINDEXH.Get() << 8) | nrf.USBD.WINDEXL.Get()),
|
||||
WLength: uint16(((nrf.USBD.WLENGTHH.Get() & 0xff) << 8) | (nrf.USBD.WLENGTHL.Get() & 0xff)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,131 +214,30 @@ func initEndpoint(ep, config uint32) {
|
||||
enableEPIn(0)
|
||||
enableEPOut(0)
|
||||
nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0)
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
SendZlp() // nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
}
|
||||
|
||||
func handleStandardSetup(setup usbSetup) bool {
|
||||
switch setup.bRequest {
|
||||
case usb_GET_STATUS:
|
||||
buf := []byte{0, 0}
|
||||
// SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data, 0)
|
||||
|
||||
if setup.bmRequestType != 0 { // endpoint
|
||||
if isEndpointHalt {
|
||||
buf[0] = 1
|
||||
}
|
||||
}
|
||||
// clear transfer complete flag
|
||||
nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4)
|
||||
|
||||
sendUSBPacket(0, buf)
|
||||
return true
|
||||
|
||||
case usb_CLEAR_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = false
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = false
|
||||
}
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
return true
|
||||
|
||||
case usb_SET_FEATURE:
|
||||
if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP
|
||||
isRemoteWakeUpEnabled = true
|
||||
} else if setup.wValueL == 0 { // ENDPOINTHALT
|
||||
isEndpointHalt = true
|
||||
}
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
return true
|
||||
|
||||
case usb_SET_ADDRESS:
|
||||
// nrf USBD handles this
|
||||
return true
|
||||
|
||||
case usb_GET_DESCRIPTOR:
|
||||
sendDescriptor(setup)
|
||||
return true
|
||||
|
||||
case usb_SET_DESCRIPTOR:
|
||||
return false
|
||||
|
||||
case usb_GET_CONFIGURATION:
|
||||
buff := []byte{usbConfiguration}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_CONFIGURATION:
|
||||
if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE {
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
for i := 1; i < len(endPoints); i++ {
|
||||
initEndpoint(uint32(i), endPoints[i])
|
||||
}
|
||||
|
||||
usbConfiguration = setup.wValueL
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
|
||||
case usb_GET_INTERFACE:
|
||||
buff := []byte{usbSetInterface}
|
||||
sendUSBPacket(0, buff)
|
||||
return true
|
||||
|
||||
case usb_SET_INTERFACE:
|
||||
usbSetInterface = setup.wValueL
|
||||
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
return true
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func cdcSetup(setup usbSetup) bool {
|
||||
if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
sendUSBPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.bRequest == usb_CDC_SET_LINE_CODING {
|
||||
epout0data_setlinecoding = true
|
||||
nrf.USBD.TASKS_EP0RCVOUT.Set(1)
|
||||
return true
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.wValueL
|
||||
checkShouldReset()
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
|
||||
if setup.bRequest == usb_CDC_SEND_BREAK {
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte) {
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
count := len(data)
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data)
|
||||
if 0 < int(maxsize) && int(maxsize) < count {
|
||||
count = int(maxsize)
|
||||
}
|
||||
copy(udd_ep_in_cache_buffer[ep][:], data[:count])
|
||||
if ep == 0 && count > usbEndpointPacketSize {
|
||||
sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[ep][usbEndpointPacketSize]
|
||||
sendOnEP0DATADONE.offset = usbEndpointPacketSize
|
||||
sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[ep][sendOnEP0DATADONE.offset]
|
||||
sendOnEP0DATADONE.count = count - usbEndpointPacketSize
|
||||
count = usbEndpointPacketSize
|
||||
}
|
||||
@@ -498,20 +248,21 @@ func sendUSBPacket(ep uint32, data []byte) {
|
||||
)
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) handleEndpoint(ep uint32) {
|
||||
func handleEndpointRx(ep uint32) []byte {
|
||||
// get data
|
||||
count := int(nrf.USBD.EPOUT[ep].AMOUNT.Get())
|
||||
|
||||
// move to ring buffer
|
||||
for i := 0; i < count; i++ {
|
||||
usbcdc.Receive(byte(udd_ep_out_cache_buffer[ep][i]))
|
||||
}
|
||||
buf := make([]byte, count)
|
||||
copy(buf, udd_ep_out_cache_buffer[ep][:])
|
||||
|
||||
// set ready for next data
|
||||
nrf.USBD.SIZE.EPOUT[ep].Set(0)
|
||||
|
||||
return buf[:count]
|
||||
}
|
||||
|
||||
func sendZlp() {
|
||||
func SendZlp() {
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
}
|
||||
|
||||
@@ -532,3 +283,52 @@ func enableEPIn(ep uint32) {
|
||||
epinen = epinen | (nrf.USBD_EPINEN_IN0 << ep)
|
||||
nrf.USBD.EPINEN.Set(epinen)
|
||||
}
|
||||
|
||||
func handleUSBSetAddress(setup USBSetup) bool {
|
||||
// nrf USBD handles this
|
||||
return true
|
||||
}
|
||||
|
||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
|
||||
nrf.USBD.TASKS_EP0RCVOUT.Set(1)
|
||||
|
||||
nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0]))))
|
||||
nrf.USBD.EPOUT[0].MAXCNT.Set(64)
|
||||
|
||||
timeout := 300000
|
||||
count := 0
|
||||
for {
|
||||
if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 {
|
||||
nrf.USBD.EVENTS_EP0DATADONE.Set(0)
|
||||
count = int(nrf.USBD.SIZE.EPOUT[0].Get())
|
||||
nrf.USBD.TASKS_STARTEPOUT[0].Set(1)
|
||||
break
|
||||
}
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
timeout = 300000
|
||||
for {
|
||||
if nrf.USBD.EVENTS_ENDEPOUT[0].Get() == 1 {
|
||||
nrf.USBD.EVENTS_ENDEPOUT[0].Set(0)
|
||||
break
|
||||
}
|
||||
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return b, errUSBCDCReadTimeout
|
||||
}
|
||||
}
|
||||
|
||||
nrf.USBD.TASKS_EP0STATUS.Set(1)
|
||||
nrf.USBD.TASKS_EP0RCVOUT.Set(0)
|
||||
|
||||
copy(b[:7], udd_ep_out_cache_buffer[0][:count])
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
@@ -14,12 +14,10 @@ const (
|
||||
DFU_MAGIC_OTA_RESET = 0xA8
|
||||
)
|
||||
|
||||
// checkShouldReset is called by the USB-CDC implementation to check whether to
|
||||
// reset into the bootloader/OTA and if so, resets the chip appropriately.
|
||||
func checkShouldReset() {
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
EnterUF2Bootloader()
|
||||
}
|
||||
// ResetProcessor should perform a system reset in preparation
|
||||
// to switch to the bootloader to flash new firmware.
|
||||
func ResetProcessor() {
|
||||
EnterUF2Bootloader()
|
||||
}
|
||||
|
||||
// EnterSerialBootloader resets the chip into the serial bootloader. After
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
//go:build rp2040
|
||||
// +build rp2040
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/rp"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// these are rp2040 specific.
|
||||
)
|
||||
|
||||
var (
|
||||
sendOnEP0DATADONE struct {
|
||||
offset int
|
||||
data []byte
|
||||
pid uint32
|
||||
}
|
||||
)
|
||||
|
||||
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
|
||||
func (dev *USBDevice) Configure(config UARTConfig) {
|
||||
//// Clear any previous state in dpram just in case
|
||||
//memset(usb_dpram, 0, sizeof(*usb_dpram)); // <1>
|
||||
usbDPSRAM.clear()
|
||||
|
||||
//// Enable USB interrupt at processor
|
||||
//irq_set_enabled(USBCTRL_IRQ, true);
|
||||
rp.USBCTRL_REGS.INTE.Set(0)
|
||||
intr := interrupt.New(rp.IRQ_USBCTRL_IRQ, handleUSBIRQ)
|
||||
intr.SetPriority(0x00)
|
||||
intr.Enable()
|
||||
irqSet(rp.IRQ_USBCTRL_IRQ, true)
|
||||
|
||||
//// Mux the controller to the onboard usb phy
|
||||
//usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
|
||||
rp.USBCTRL_REGS.USB_MUXING.Set(rp.USBCTRL_REGS_USB_MUXING_TO_PHY | rp.USBCTRL_REGS_USB_MUXING_SOFTCON)
|
||||
|
||||
//// Force VBUS detect so the device thinks it is plugged into a host
|
||||
//usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
|
||||
rp.USBCTRL_REGS.USB_PWR.Set(rp.USBCTRL_REGS_USB_PWR_VBUS_DETECT | rp.USBCTRL_REGS_USB_PWR_VBUS_DETECT_OVERRIDE_EN)
|
||||
|
||||
//// Enable the USB controller in device mode.
|
||||
//usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
|
||||
rp.USBCTRL_REGS.MAIN_CTRL.Set(rp.USBCTRL_REGS_MAIN_CTRL_CONTROLLER_EN)
|
||||
|
||||
//// Enable an interrupt per EP0 transaction
|
||||
//usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS; // <2>
|
||||
rp.USBCTRL_REGS.SIE_CTRL.Set(rp.USBCTRL_REGS_SIE_CTRL_EP0_INT_1BUF)
|
||||
|
||||
//// Enable interrupts for when a buffer is done, when the bus is reset,
|
||||
//// and when a setup packet is received
|
||||
//usb_hw->inte = USB_INTS_BUFF_STATUS_BITS |
|
||||
// USB_INTS_BUS_RESET_BITS |
|
||||
// USB_INTS_SETUP_REQ_BITS;
|
||||
rp.USBCTRL_REGS.INTE.Set(rp.USBCTRL_REGS_INTE_BUFF_STATUS |
|
||||
rp.USBCTRL_REGS_INTE_BUS_RESET |
|
||||
rp.USBCTRL_REGS_INTE_SETUP_REQ)
|
||||
|
||||
//// Set up endpoints (endpoint control registers)
|
||||
//// described by device configuration
|
||||
//usb_setup_endpoints();
|
||||
// void usb_setup_endpoints() {
|
||||
// const struct usb_endpoint_configuration *endpoints = dev_config.endpoints;
|
||||
// for (int i = 0; i < USB_NUM_ENDPOINTS; i++) {
|
||||
// if (endpoints[i].descriptor && endpoints[i].handler) {
|
||||
// usb_setup_endpoint(&endpoints[i]);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void usb_setup_endpoint(const struct usb_endpoint_configuration *ep) {
|
||||
// printf("Set up endpoint 0x%x with buffer address 0x%p\n", ep->descriptor->bEndpointAddress, ep->data_buffer);
|
||||
//
|
||||
// // EP0 doesn't have one so return if that is the case
|
||||
// if (!ep->endpoint_control) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Get the data buffer as an offset of the USB controller's DPRAM
|
||||
// uint32_t dpram_offset = usb_buffer_offset(ep->data_buffer);
|
||||
// uint32_t reg = EP_CTRL_ENABLE_BITS
|
||||
// | EP_CTRL_INTERRUPT_PER_BUFFER
|
||||
// | (ep->descriptor->bmAttributes << EP_CTRL_BUFFER_TYPE_LSB)
|
||||
// | dpram_offset;
|
||||
// *ep->endpoint_control = reg;
|
||||
// }
|
||||
|
||||
//// Present full speed device by enabling pull up on DP
|
||||
//usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
|
||||
rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN)
|
||||
|
||||
// 追加
|
||||
val := uint32(0)
|
||||
val |= 0x00000400
|
||||
usbDPSRAM.EP0OutBufferControl = USBBufferControlRegister(val)
|
||||
}
|
||||
|
||||
func handleUSBIRQ(intr interrupt.Interrupt) {
|
||||
status := rp.USBCTRL_REGS.INTS.Get()
|
||||
|
||||
// setup
|
||||
//rp.USBCTRL_REGS.SIE_STATUS.Set(0xFFFFFFFF)
|
||||
|
||||
//if false {
|
||||
// // SOF
|
||||
// x := rp.USBCTRL_REGS.SOF_RD.Get()
|
||||
//}
|
||||
|
||||
// void isr_usbctrl(void) {
|
||||
// // USB interrupt handler
|
||||
// uint32_t status = usb_hw->ints;
|
||||
// uint32_t handled = 0;
|
||||
|
||||
// // Setup packet received
|
||||
// if (status & USB_INTS_SETUP_REQ_BITS) {
|
||||
// handled |= USB_INTS_SETUP_REQ_BITS;
|
||||
// usb_hw_clear->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
|
||||
// usb_handle_setup_packet();
|
||||
// }
|
||||
// /// \end::isr_setup_packet[]
|
||||
if (status & rp.USBCTRL_REGS_INTS_SETUP_REQ) > 0 {
|
||||
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_SETUP_REC)
|
||||
setup := newUSBSetup(usbDPSRAM.Setup[:])
|
||||
|
||||
ok := false
|
||||
if (setup.BmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD {
|
||||
// Standard Requests
|
||||
ok = handleStandardSetup(setup)
|
||||
} else {
|
||||
// Class Interface Requests
|
||||
if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil {
|
||||
ok = callbackUSBSetup[setup.WIndex](setup)
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
// set Bank1 ready
|
||||
//setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY)
|
||||
} else {
|
||||
// Stall endpoint
|
||||
//setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
}
|
||||
|
||||
//if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 {
|
||||
// // ack the stall
|
||||
// setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1)
|
||||
|
||||
// // clear stall request
|
||||
// setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1)
|
||||
//}
|
||||
}
|
||||
|
||||
// // Buffer status, one or more buffers have completed
|
||||
// if (status & USB_INTS_BUFF_STATUS_BITS) {
|
||||
// handled |= USB_INTS_BUFF_STATUS_BITS;
|
||||
// usb_handle_buff_status();
|
||||
// }
|
||||
if (status & rp.USBCTRL_REGS_INTS_BUFF_STATUS) > 0 {
|
||||
if sendOnEP0DATADONE.offset > 0 {
|
||||
ep := uint32(0)
|
||||
data := sendOnEP0DATADONE.data
|
||||
count := len(data) - sendOnEP0DATADONE.offset
|
||||
if ep == 0 && count > usbEndpointPacketSize {
|
||||
count = usbEndpointPacketSize
|
||||
}
|
||||
sendViaEPIn(ep, data[sendOnEP0DATADONE.offset:], count, 0)
|
||||
sendOnEP0DATADONE.offset += count
|
||||
if sendOnEP0DATADONE.offset == len(data) {
|
||||
sendOnEP0DATADONE.offset = 0
|
||||
}
|
||||
}
|
||||
//s2 := rp.USBCTRL_REGS.BUFF_STATUS.Get()
|
||||
//if (s2 & 0x00000001) > 0 {
|
||||
// // EP0_IN
|
||||
//} else if (s2 & 0x00000002) > 0 {
|
||||
// // EP0_OUT
|
||||
// //sendUSBPacket(0, []byte{}, 0)
|
||||
//}
|
||||
rp.USBCTRL_REGS.BUFF_STATUS.Set(0xFFFFFFFF)
|
||||
}
|
||||
|
||||
// // Bus is reset
|
||||
// if (status & USB_INTS_BUS_RESET_BITS) {
|
||||
// printf("BUS RESET\n");
|
||||
// handled |= USB_INTS_BUS_RESET_BITS;
|
||||
// usb_hw_clear->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
|
||||
// usb_bus_reset();
|
||||
// }
|
||||
// void usb_bus_reset(void) {
|
||||
// // Set address back to 0
|
||||
// dev_addr = 0;
|
||||
// should_set_address = false;
|
||||
// usb_hw->dev_addr_ctrl = 0;
|
||||
// configured = false;
|
||||
// }
|
||||
if (status & rp.USBCTRL_REGS_INTS_BUS_RESET) > 0 {
|
||||
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_BUS_RESET)
|
||||
}
|
||||
|
||||
//
|
||||
// if (status ^ handled) {
|
||||
// panic("Unhandled IRQ 0x%x\n", (uint) (status ^ handled));
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
func initEndpoint(ep, config uint32) {
|
||||
if ep == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
val := uint32(0x80000000) | uint32(0x20000000)
|
||||
offset := (ep-1)*64 + 0x180
|
||||
val |= offset
|
||||
|
||||
switch config {
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn:
|
||||
val |= 0x0C000000
|
||||
// ep1 in
|
||||
usbDPSRAM.EP1InControl = USBEndpointControlRegister(val)
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointOut:
|
||||
val |= 0x08000000
|
||||
// ep2 out
|
||||
usbDPSRAM.EP2OutControl = USBEndpointControlRegister(val)
|
||||
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
|
||||
// TODO: not really anything, seems like...
|
||||
|
||||
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
|
||||
val |= 0x08000000
|
||||
usbDPSRAM.EP3InControl = USBEndpointControlRegister(val)
|
||||
// ep3 in
|
||||
case usb_ENDPOINT_TYPE_CONTROL:
|
||||
// skip
|
||||
}
|
||||
}
|
||||
|
||||
func handleUSBSetAddress(setup USBSetup) bool {
|
||||
// setup.BmRequestType, setup.BRequest, setup.WValueL, setup.WValueH, setup.WIndex, setup.WLength)
|
||||
sendUSBPacket(0, []byte{}, 0)
|
||||
|
||||
// last, set the device address to that requested by host
|
||||
// wait for transfer to complete
|
||||
timeout := 3000
|
||||
rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_ACK_REC)
|
||||
for (rp.USBCTRL_REGS.SIE_STATUS.Get() & rp.USBCTRL_REGS_SIE_STATUS_ACK_REC) == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
rp.USBCTRL_REGS.ADDR_ENDP.Set(uint32(setup.WValueL) & rp.USBCTRL_REGS_ADDR_ENDP_ADDRESS_Msk)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// SendUSBInPacket sends a packet for USB (interrupt in / bulk in).
|
||||
func SendUSBInPacket(ep uint32, data []byte) bool {
|
||||
sendUSBPacket(ep, data, 0)
|
||||
return true
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func sendUSBPacket(ep uint32, data []byte, maxsize uint16) {
|
||||
count := len(data)
|
||||
if 0 < int(maxsize) && int(maxsize) < count {
|
||||
count = int(maxsize)
|
||||
}
|
||||
|
||||
if ep == 0 {
|
||||
if count > usbEndpointPacketSize {
|
||||
count = usbEndpointPacketSize
|
||||
|
||||
sendOnEP0DATADONE.offset = count
|
||||
sendOnEP0DATADONE.data = data
|
||||
} else {
|
||||
sendOnEP0DATADONE.offset = 0
|
||||
}
|
||||
}
|
||||
|
||||
sendViaEPIn(ep, data, count, 1)
|
||||
}
|
||||
|
||||
func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) {
|
||||
var b [cdcLineInfoSize]byte
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func handleEndpointRx(ep uint32) []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendZlp() {
|
||||
sendUSBPacket(0, []byte{}, 0)
|
||||
}
|
||||
|
||||
var ep3data0 = true
|
||||
|
||||
func sendViaEPIn(ep uint32, data []byte, count int, pid int) {
|
||||
// void usb_start_transfer(struct usb_endpoint_configuration *ep, uint8_t *buf, uint16_t len) {
|
||||
// // We are asserting that the length is <= 64 bytes for simplicity of the example.
|
||||
// // For multi packet transfers see the tinyusb port.
|
||||
// assert(len <= 64);
|
||||
//
|
||||
// printf("Start transfer of len %d on ep addr 0x%x\n", len, ep->descriptor->bEndpointAddress);
|
||||
//
|
||||
// // Prepare buffer control register value
|
||||
// uint32_t val = len | USB_BUF_CTRL_AVAIL;
|
||||
val := uint32(count) | 0x00000400
|
||||
|
||||
// if (ep_is_tx(ep)) {
|
||||
// // Need to copy the data from the user buffer to the usb memory
|
||||
// memcpy((void *) ep->data_buffer, (void *) buf, len);
|
||||
// DATA0 or DATA1
|
||||
if ep == 3 {
|
||||
if ep3data0 {
|
||||
val |= 0x00002000
|
||||
}
|
||||
ep3data0 = !ep3data0
|
||||
} else if pid == 1 {
|
||||
val |= 0x00002000
|
||||
}
|
||||
|
||||
// // Mark as full
|
||||
// val |= USB_BUF_CTRL_FULL;
|
||||
val |= 0x00008000
|
||||
|
||||
// }
|
||||
// // Set pid and flip for next transfer
|
||||
// val |= ep->next_pid ? USB_BUF_CTRL_DATA1_PID : USB_BUF_CTRL_DATA0_PID;
|
||||
// ep->next_pid ^= 1u;
|
||||
//
|
||||
// *ep->buffer_control = val;
|
||||
switch ep & 0x7F {
|
||||
case 0:
|
||||
copy(usbDPSRAM.EP0Buffer0[:], data[:count])
|
||||
usbDPSRAM.EP0InBufferControl = USBBufferControlRegister(val)
|
||||
case 1:
|
||||
copy(usbDPSRAM.EP1Buffer[:], data[:count])
|
||||
usbDPSRAM.EP1InBufferControl = USBBufferControlRegister(val)
|
||||
case 2:
|
||||
usbDPSRAM.EP2OutBufferControl = USBBufferControlRegister(val)
|
||||
case 3:
|
||||
copy(usbDPSRAM.EP3Buffer[:], data[:count])
|
||||
usbDPSRAM.EP3InBufferControl = USBBufferControlRegister(val)
|
||||
default:
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
// ResetProcessor should perform a system reset in preparation
|
||||
// to switch to the bootloader to flash new firmware.
|
||||
func ResetProcessor() {
|
||||
arm.DisableInterrupts()
|
||||
|
||||
//// Perform magic reset into bootloader, as mentioned in
|
||||
//// https://github.com/arduino/ArduinoCore-samd/issues/197
|
||||
//*(*uint32)(unsafe.Pointer(uintptr(0x20000000 + HSRAM_SIZE - 4))) = RESET_MAGIC_VALUE
|
||||
|
||||
arm.SystemReset()
|
||||
}
|
||||
|
||||
type USBDPSRAM struct {
|
||||
Setup [8]byte
|
||||
|
||||
EP1InControl USBEndpointControlRegister // 0x0008
|
||||
EP1OutControl USBEndpointControlRegister // 0x000c
|
||||
EP2InControl USBEndpointControlRegister // 0x0010
|
||||
EP2OutControl USBEndpointControlRegister // 0x0014
|
||||
EP3InControl USBEndpointControlRegister // 0x0018
|
||||
EP3OutControl USBEndpointControlRegister // 0x001c
|
||||
EP4InControl USBEndpointControlRegister // 0x0020
|
||||
EP4OutControl USBEndpointControlRegister // 0x0024
|
||||
EP5InControl USBEndpointControlRegister // 0x0028
|
||||
EP5OutControl USBEndpointControlRegister // 0x002c
|
||||
EP6InControl USBEndpointControlRegister // 0x0030
|
||||
EP6OutControl USBEndpointControlRegister // 0x0034
|
||||
EP7InControl USBEndpointControlRegister // 0x0038
|
||||
EP7OutControl USBEndpointControlRegister // 0x003c
|
||||
EP8InControl USBEndpointControlRegister // 0x0040
|
||||
EP8OutControl USBEndpointControlRegister // 0x0044
|
||||
EP9InControl USBEndpointControlRegister // 0x0048
|
||||
EP9OutControl USBEndpointControlRegister // 0x004c
|
||||
EP10InControl USBEndpointControlRegister // 0x0050
|
||||
EP10OutControl USBEndpointControlRegister // 0x0054
|
||||
EP11InControl USBEndpointControlRegister // 0x0058
|
||||
EP11OutControl USBEndpointControlRegister // 0x005c
|
||||
EP12InControl USBEndpointControlRegister // 0x0060
|
||||
EP12OutControl USBEndpointControlRegister // 0x0064
|
||||
EP13InControl USBEndpointControlRegister // 0x0068
|
||||
EP13OutControl USBEndpointControlRegister // 0x006c
|
||||
EP14InControl USBEndpointControlRegister // 0x0070
|
||||
EP14OutControl USBEndpointControlRegister // 0x0074
|
||||
EP15InControl USBEndpointControlRegister // 0x0078
|
||||
EP15OutControl USBEndpointControlRegister // 0x007c
|
||||
|
||||
EP0InBufferControl USBBufferControlRegister // 0x0080
|
||||
EP0OutBufferControl USBBufferControlRegister // 0x0084
|
||||
EP1InBufferControl USBBufferControlRegister // 0x0088
|
||||
EP1OutBufferControl USBBufferControlRegister // 0x008c
|
||||
EP2InBufferControl USBBufferControlRegister // 0x0090
|
||||
EP2OutBufferControl USBBufferControlRegister // 0x0094
|
||||
EP3InBufferControl USBBufferControlRegister // 0x0098
|
||||
EP3OutBufferControl USBBufferControlRegister // 0x009c
|
||||
EP4InBufferControl USBBufferControlRegister // 0x00a0
|
||||
EP4OutBufferControl USBBufferControlRegister // 0x00a4
|
||||
EP5InBufferControl USBBufferControlRegister // 0x00a8
|
||||
EP5OutBufferControl USBBufferControlRegister // 0x00ac
|
||||
EP6InBufferControl USBBufferControlRegister // 0x00b0
|
||||
EP6OutBufferControl USBBufferControlRegister // 0x00b4
|
||||
EP7InBufferControl USBBufferControlRegister // 0x00b8
|
||||
EP7OutBufferControl USBBufferControlRegister // 0x00bc
|
||||
EP8InBufferControl USBBufferControlRegister // 0x00c0
|
||||
EP8OutBufferControl USBBufferControlRegister // 0x00c4
|
||||
EP9InBufferControl USBBufferControlRegister // 0x00c8
|
||||
EP9OutBufferControl USBBufferControlRegister // 0x00cc
|
||||
EP10InBufferControl USBBufferControlRegister // 0x00d0
|
||||
EP10OutBufferControl USBBufferControlRegister // 0x00d4
|
||||
EP11InBufferControl USBBufferControlRegister // 0x00d8
|
||||
EP11OutBufferControl USBBufferControlRegister // 0x00dc
|
||||
EP12InBufferControl USBBufferControlRegister // 0x00e0
|
||||
EP12OutBufferControl USBBufferControlRegister // 0x00e4
|
||||
EP13InBufferControl USBBufferControlRegister // 0x00e8
|
||||
EP13OutBufferControl USBBufferControlRegister // 0x00ec
|
||||
EP14InBufferControl USBBufferControlRegister // 0x00f0
|
||||
EP14OutBufferControl USBBufferControlRegister // 0x00f4
|
||||
EP15InBufferControl USBBufferControlRegister // 0x00f8
|
||||
EP15OutBufferControl USBBufferControlRegister // 0x00fc
|
||||
|
||||
// offset : 0x100
|
||||
EP0Buffer0 [64]byte
|
||||
EP0Buffer1 [64]byte
|
||||
|
||||
// offset : 0x180 ..
|
||||
EP1Buffer [64]byte
|
||||
EP2Buffer [64]byte
|
||||
EP3Buffer [64]byte
|
||||
EP4Buffer [64]byte
|
||||
EP5Buffer [64]byte
|
||||
EP6Buffer [64]byte
|
||||
EP7Buffer [64]byte
|
||||
}
|
||||
|
||||
type USBEndpointControlRegister uint32
|
||||
type USBBufferControlRegister uint32
|
||||
|
||||
var usbDPSRAM = (*USBDPSRAM)(unsafe.Pointer(uintptr(0x50100000)))
|
||||
|
||||
func (d *USBDPSRAM) clear() {
|
||||
for i := range d.Setup {
|
||||
d.Setup[i] = 0
|
||||
}
|
||||
|
||||
d.EP1InControl = 0
|
||||
d.EP1OutControl = 0
|
||||
d.EP2InControl = 0
|
||||
d.EP2OutControl = 0
|
||||
d.EP3InControl = 0
|
||||
d.EP3OutControl = 0
|
||||
d.EP4InControl = 0
|
||||
d.EP4OutControl = 0
|
||||
d.EP5InControl = 0
|
||||
d.EP5OutControl = 0
|
||||
d.EP6InControl = 0
|
||||
d.EP6OutControl = 0
|
||||
d.EP7InControl = 0
|
||||
d.EP7OutControl = 0
|
||||
d.EP8InControl = 0
|
||||
d.EP8OutControl = 0
|
||||
d.EP9InControl = 0
|
||||
d.EP9OutControl = 0
|
||||
d.EP10InControl = 0
|
||||
d.EP10OutControl = 0
|
||||
d.EP11InControl = 0
|
||||
d.EP11OutControl = 0
|
||||
d.EP12InControl = 0
|
||||
d.EP12OutControl = 0
|
||||
d.EP13InControl = 0
|
||||
d.EP13OutControl = 0
|
||||
d.EP14InControl = 0
|
||||
d.EP14OutControl = 0
|
||||
d.EP15InControl = 0
|
||||
d.EP15OutControl = 0
|
||||
|
||||
d.EP0InBufferControl = 0
|
||||
d.EP0OutBufferControl = 0
|
||||
d.EP1InBufferControl = 0
|
||||
d.EP1OutBufferControl = 0
|
||||
d.EP2InBufferControl = 0
|
||||
d.EP2OutBufferControl = 0
|
||||
d.EP3InBufferControl = 0
|
||||
d.EP3OutBufferControl = 0
|
||||
d.EP4InBufferControl = 0
|
||||
d.EP4OutBufferControl = 0
|
||||
d.EP5InBufferControl = 0
|
||||
d.EP5OutBufferControl = 0
|
||||
d.EP6InBufferControl = 0
|
||||
d.EP6OutBufferControl = 0
|
||||
d.EP7InBufferControl = 0
|
||||
d.EP7OutBufferControl = 0
|
||||
d.EP8InBufferControl = 0
|
||||
d.EP8OutBufferControl = 0
|
||||
d.EP9InBufferControl = 0
|
||||
d.EP9OutBufferControl = 0
|
||||
d.EP10InBufferControl = 0
|
||||
d.EP10OutBufferControl = 0
|
||||
d.EP11InBufferControl = 0
|
||||
d.EP11OutBufferControl = 0
|
||||
d.EP12InBufferControl = 0
|
||||
d.EP12OutBufferControl = 0
|
||||
d.EP13InBufferControl = 0
|
||||
d.EP13OutBufferControl = 0
|
||||
d.EP14InBufferControl = 0
|
||||
d.EP14OutBufferControl = 0
|
||||
d.EP15InBufferControl = 0
|
||||
d.EP15OutBufferControl = 0
|
||||
}
|
||||
@@ -5,6 +5,3 @@ package machine
|
||||
|
||||
// Serial is a null device: writes to it are ignored.
|
||||
var Serial = NullSerial{}
|
||||
|
||||
func InitSerial() {
|
||||
}
|
||||
|
||||
@@ -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{})
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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{})
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package cdc
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const bufferSize = 128
|
||||
|
||||
// RingBuffer is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
type RingBuffer struct {
|
||||
rxbuffer [bufferSize]volatile.Register8
|
||||
head volatile.Register8
|
||||
tail volatile.Register8
|
||||
}
|
||||
|
||||
// NewRingBuffer returns a new ring buffer.
|
||||
func NewRingBuffer() *RingBuffer {
|
||||
return &RingBuffer{}
|
||||
}
|
||||
|
||||
// Used returns how many bytes in buffer have been used.
|
||||
func (rb *RingBuffer) Used() uint8 {
|
||||
return uint8(rb.head.Get() - rb.tail.Get())
|
||||
}
|
||||
|
||||
// Put stores a byte in the buffer. If the buffer is already
|
||||
// full, the method will return false.
|
||||
func (rb *RingBuffer) Put(val byte) bool {
|
||||
if rb.Used() != bufferSize {
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
rb.rxbuffer[rb.head.Get()%bufferSize].Set(val)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get returns a byte from the buffer. If the buffer is empty,
|
||||
// the method will return a false as the second value.
|
||||
func (rb *RingBuffer) Get() (byte, bool) {
|
||||
if rb.Used() != 0 {
|
||||
rb.tail.Set(rb.tail.Get() + 1)
|
||||
return rb.rxbuffer[rb.tail.Get()%bufferSize].Get(), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Clear resets the head and tail pointer to zero.
|
||||
func (rb *RingBuffer) Clear() {
|
||||
rb.head.Set(0)
|
||||
rb.tail.Set(0)
|
||||
}
|
||||
|
||||
const bufferSize2 = 8
|
||||
|
||||
// RingBuffer2 is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
type RingBuffer2 struct {
|
||||
rxbuffer [bufferSize2]struct {
|
||||
buf [64]byte
|
||||
size int
|
||||
}
|
||||
head volatile.Register8
|
||||
tail volatile.Register8
|
||||
}
|
||||
|
||||
// NewRingBuffer returns a new ring buffer.
|
||||
func NewRingBuffer2() *RingBuffer2 {
|
||||
return &RingBuffer2{}
|
||||
}
|
||||
|
||||
// Used returns how many bytes in buffer have been used.
|
||||
func (rb *RingBuffer2) Used() uint8 {
|
||||
return uint8(rb.head.Get() - rb.tail.Get())
|
||||
}
|
||||
|
||||
// Put stores a byte in the buffer. If the buffer is already
|
||||
// full, the method will return false.
|
||||
func (rb *RingBuffer2) Put(val []byte) bool {
|
||||
if rb.Used() == bufferSize2 {
|
||||
return false
|
||||
}
|
||||
|
||||
if rb.Used() == 0 {
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
rb.rxbuffer[rb.head.Get()%bufferSize2].size = 0
|
||||
}
|
||||
buf := &rb.rxbuffer[rb.head.Get()%bufferSize2]
|
||||
|
||||
for i := 0; i < len(val); i++ {
|
||||
if buf.size == 64 {
|
||||
// next
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
buf = &rb.rxbuffer[rb.head.Get()%bufferSize2]
|
||||
rb.rxbuffer[rb.head.Get()%bufferSize2].size = 0
|
||||
}
|
||||
buf.buf[buf.size] = val[i]
|
||||
buf.size++
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Get returns a byte from the buffer. If the buffer is empty,
|
||||
// the method will return a false as the second value.
|
||||
func (rb *RingBuffer2) Get() ([]byte, bool) {
|
||||
if rb.Used() != 0 {
|
||||
rb.tail.Set(rb.tail.Get() + 1)
|
||||
size := rb.rxbuffer[rb.tail.Get()%bufferSize2].size
|
||||
return rb.rxbuffer[rb.tail.Get()%bufferSize2].buf[:size], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Clear resets the head and tail pointer to zero.
|
||||
func (rb *RingBuffer2) Clear() {
|
||||
rb.head.Set(0)
|
||||
rb.tail.Set(0)
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package cdc
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
const (
|
||||
cdcEndpointACM = 1
|
||||
cdcEndpointOut = 2
|
||||
cdcEndpointIn = 3
|
||||
)
|
||||
|
||||
var CDC *cdc
|
||||
|
||||
type cdc struct {
|
||||
buf *RingBuffer
|
||||
callbackFuncRx func([]byte)
|
||||
}
|
||||
|
||||
// New returns hid-mouse.
|
||||
func New() *USBCDC {
|
||||
USB = &USBCDC{
|
||||
Buffer: NewRingBuffer(),
|
||||
Buffer2: NewRingBuffer2(),
|
||||
}
|
||||
return USB
|
||||
}
|
||||
|
||||
func newCDC() *cdc {
|
||||
m := &cdc{
|
||||
buf: NewRingBuffer(),
|
||||
}
|
||||
//machine.EnableCDC(m.Callback, m.CallbackRx)
|
||||
return m
|
||||
}
|
||||
|
||||
func (c *cdc) SetCallback(callbackRx func([]byte)) {
|
||||
c.callbackFuncRx = callbackRx
|
||||
}
|
||||
|
||||
func (c *cdc) Write(b []byte) (n int, err error) {
|
||||
i := 0
|
||||
for i = 0; i < len(b); i++ {
|
||||
c.buf.Put(b[i])
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (c *cdc) sendUSBPacket(b []byte) {
|
||||
machine.SendUSBInPacket(cdcEndpointIn, b)
|
||||
}
|
||||
|
||||
// from BulkIn
|
||||
func (c *cdc) Callback() {
|
||||
if b, ok := c.buf.Get(); ok {
|
||||
c.sendUSBPacket([]byte{b})
|
||||
}
|
||||
}
|
||||
|
||||
// from BulkOut
|
||||
func (c *cdc) CallbackRx(b []byte) {
|
||||
if c.callbackFuncRx != nil {
|
||||
c.callbackFuncRx(b)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// bmRequestType
|
||||
usb_REQUEST_HOSTTODEVICE = 0x00
|
||||
usb_REQUEST_DEVICETOHOST = 0x80
|
||||
usb_REQUEST_DIRECTION = 0x80
|
||||
|
||||
usb_REQUEST_STANDARD = 0x00
|
||||
usb_REQUEST_CLASS = 0x20
|
||||
usb_REQUEST_VENDOR = 0x40
|
||||
usb_REQUEST_TYPE = 0x60
|
||||
|
||||
usb_REQUEST_DEVICE = 0x00
|
||||
usb_REQUEST_INTERFACE = 0x01
|
||||
usb_REQUEST_ENDPOINT = 0x02
|
||||
usb_REQUEST_OTHER = 0x03
|
||||
usb_REQUEST_RECIPIENT = 0x1F
|
||||
|
||||
usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
|
||||
usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE = (usb_REQUEST_HOSTTODEVICE | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE)
|
||||
usb_REQUEST_DEVICETOHOST_STANDARD_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_STANDARD | usb_REQUEST_INTERFACE)
|
||||
|
||||
// CDC Class requests
|
||||
usb_CDC_SET_LINE_CODING = 0x20
|
||||
usb_CDC_GET_LINE_CODING = 0x21
|
||||
usb_CDC_SET_CONTROL_LINE_STATE = 0x22
|
||||
usb_CDC_SEND_BREAK = 0x23
|
||||
|
||||
usb_CDC_V1_10 = 0x0110
|
||||
usb_CDC_COMMUNICATION_INTERFACE_CLASS = 0x02
|
||||
|
||||
usb_CDC_CALL_MANAGEMENT = 0x01
|
||||
usb_CDC_ABSTRACT_CONTROL_MODEL = 0x02
|
||||
usb_CDC_HEADER = 0x00
|
||||
usb_CDC_ABSTRACT_CONTROL_MANAGEMENT = 0x02
|
||||
usb_CDC_UNION = 0x06
|
||||
usb_CDC_CS_INTERFACE = 0x24
|
||||
usb_CDC_CS_ENDPOINT = 0x25
|
||||
usb_CDC_DATA_INTERFACE_CLASS = 0x0A
|
||||
|
||||
usb_CDC_LINESTATE_DTR = 0x01
|
||||
usb_CDC_LINESTATE_RTS = 0x02
|
||||
)
|
||||
|
||||
func (c *cdc) handleSetup(setup machine.USBSetup) bool {
|
||||
if setup.BmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.BRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
//c.sendUSBPacket(0, b[:], setup.WLength)
|
||||
c.sendUSBPacket(b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.BmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.BRequest == usb_CDC_SET_LINE_CODING {
|
||||
b, err := machine.ReceiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.WValueL
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SET_LINE_CODING || setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
// auto-reset into the bootloader
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
machine.ResetProcessor()
|
||||
} else {
|
||||
// TODO: cancel any reset
|
||||
}
|
||||
sendZlp()
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SEND_BREAK {
|
||||
// TODO: something with this value?
|
||||
// breakValue = ((uint16_t)setup.WValueH << 8) | setup.WValueL;
|
||||
// return false;
|
||||
sendZlp()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package cdc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
var (
|
||||
errUSBCDCBufferEmpty = errors.New("USB-CDC buffer empty")
|
||||
errUSBCDCWriteByteTimeout = errors.New("USB-CDC write byte timeout")
|
||||
errUSBCDCReadTimeout = errors.New("USB-CDC read timeout")
|
||||
errUSBCDCBytesRead = errors.New("USB-CDC invalid number of bytes read")
|
||||
)
|
||||
|
||||
const cdcLineInfoSize = 7
|
||||
|
||||
type cdcLineInfo struct {
|
||||
dwDTERate uint32
|
||||
bCharFormat uint8
|
||||
bParityType uint8
|
||||
bDataBits uint8
|
||||
lineState uint8
|
||||
}
|
||||
|
||||
// USBCDC is the serial interface that works over the USB port.
|
||||
// To implement the USBCDC interface for a board, you must declare a concrete type as follows:
|
||||
//
|
||||
// type USBCDC struct {
|
||||
// Buffer *RingBuffer
|
||||
// }
|
||||
//
|
||||
// You can also add additional members to this struct depending on your implementation,
|
||||
// but the *RingBuffer is required.
|
||||
// When you are declaring the USBCDC for your board, make sure that you also declare the
|
||||
// RingBuffer using the NewRingBuffer() function:
|
||||
//
|
||||
// USBCDC{Buffer: NewRingBuffer()}
|
||||
//
|
||||
|
||||
// Read from the RX buffer.
|
||||
func (usbcdc *USBCDC) Read(data []byte) (n int, err error) {
|
||||
// check if RX buffer is empty
|
||||
size := usbcdc.Buffered()
|
||||
if size == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Make sure we do not read more from buffer than the data slice can hold.
|
||||
if len(data) < size {
|
||||
size = len(data)
|
||||
}
|
||||
|
||||
// only read number of bytes used from buffer
|
||||
for i := 0; i < size; i++ {
|
||||
v, _ := usbcdc.ReadByte()
|
||||
data[i] = v
|
||||
}
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// ReadByte reads a single byte from the RX buffer.
|
||||
// If there is no data in the buffer, returns an error.
|
||||
func (usbcdc *USBCDC) ReadByte() (byte, error) {
|
||||
// check if RX buffer is empty
|
||||
buf, ok := usbcdc.Buffer.Get()
|
||||
if !ok {
|
||||
return 0, errUSBCDCBufferEmpty
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes currently stored in the RX buffer.
|
||||
func (usbcdc *USBCDC) Buffered() int {
|
||||
return int(usbcdc.Buffer.Used())
|
||||
}
|
||||
|
||||
// Receive handles adding data to the UART's data buffer.
|
||||
// Usually called by the IRQ handler for a machine.
|
||||
func (usbcdc *USBCDC) Receive(data byte) {
|
||||
usbcdc.Buffer.Put(data)
|
||||
}
|
||||
|
||||
// USBCDC is the USB CDC aka serial over USB interface on the SAMD21.
|
||||
type USBCDC struct {
|
||||
Buffer *RingBuffer
|
||||
Buffer2 *RingBuffer2
|
||||
TxIdx volatile.Register8
|
||||
waitTxcRetryCount uint8
|
||||
sent bool
|
||||
configured bool
|
||||
waitTxc bool
|
||||
}
|
||||
|
||||
func (x *USBCDC) Debug() int {
|
||||
return 3
|
||||
}
|
||||
|
||||
var (
|
||||
// USB is a USB CDC interface.
|
||||
USB *USBCDC
|
||||
|
||||
usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00}
|
||||
)
|
||||
|
||||
// Configure the USB CDC interface. The config is here for compatibility with the UART interface.
|
||||
func (usbcdc *USBCDC) Configure(config machine.UARTConfig) {
|
||||
}
|
||||
|
||||
// Flush flushes buffered data.
|
||||
func (usbcdc *USBCDC) Flush() {
|
||||
mask := interrupt.Disable()
|
||||
if b, ok := usbcdc.Buffer2.Get(); ok {
|
||||
machine.SendUSBInPacket(cdcEndpointIn, b)
|
||||
} else {
|
||||
usbcdc.waitTxc = false
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
|
||||
// Write data to the USBCDC.
|
||||
func (usbcdc *USBCDC) Write(data []byte) (n int, err error) {
|
||||
if usbLineInfo.lineState > 0 {
|
||||
mask := interrupt.Disable()
|
||||
usbcdc.Buffer2.Put(data)
|
||||
if !usbcdc.waitTxc {
|
||||
usbcdc.waitTxc = true
|
||||
usbcdc.Flush()
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte of data to the USB CDC interface.
|
||||
func (usbcdc *USBCDC) WriteByte(c byte) error {
|
||||
usbcdc.Write([]byte{c})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) DTR() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0
|
||||
}
|
||||
|
||||
func (usbcdc *USBCDC) RTS() bool {
|
||||
return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0
|
||||
}
|
||||
|
||||
// Configured returns whether usbcdc is configured or not.
|
||||
func (usbcdc *USBCDC) Configured() bool {
|
||||
return usbcdc.configured
|
||||
}
|
||||
|
||||
func cdcCallbackRx(b []byte) {
|
||||
for i := range b {
|
||||
USB.Receive(b[i])
|
||||
}
|
||||
}
|
||||
|
||||
func cdcSetup(setup machine.USBSetup) bool {
|
||||
if setup.BmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE {
|
||||
if setup.BRequest == usb_CDC_GET_LINE_CODING {
|
||||
var b [cdcLineInfoSize]byte
|
||||
b[0] = byte(usbLineInfo.dwDTERate)
|
||||
b[1] = byte(usbLineInfo.dwDTERate >> 8)
|
||||
b[2] = byte(usbLineInfo.dwDTERate >> 16)
|
||||
b[3] = byte(usbLineInfo.dwDTERate >> 24)
|
||||
b[4] = byte(usbLineInfo.bCharFormat)
|
||||
b[5] = byte(usbLineInfo.bParityType)
|
||||
b[6] = byte(usbLineInfo.bDataBits)
|
||||
|
||||
//machine.SendUSBPacket(0, b[:], setup.WLength)
|
||||
machine.SendUSBInPacket(0, b[:])
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if setup.BmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE {
|
||||
if setup.BRequest == usb_CDC_SET_LINE_CODING {
|
||||
b, err := machine.ReceiveUSBControlPacket()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
usbLineInfo.bCharFormat = b[4]
|
||||
usbLineInfo.bParityType = b[5]
|
||||
usbLineInfo.bDataBits = b[6]
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
usbLineInfo.lineState = setup.WValueL
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SET_LINE_CODING || setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE {
|
||||
// auto-reset into the bootloader
|
||||
if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 {
|
||||
machine.ResetProcessor()
|
||||
} else {
|
||||
// TODO: cancel any reset
|
||||
}
|
||||
sendZlp()
|
||||
}
|
||||
|
||||
if setup.BRequest == usb_CDC_SEND_BREAK {
|
||||
// TODO: something with this value?
|
||||
// breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL;
|
||||
// return false;
|
||||
sendZlp()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func EnableUSBCDC() {
|
||||
machine.Serial = USB
|
||||
machine.EnableCDC(USB.Flush, cdcCallbackRx, cdcSetup)
|
||||
}
|
||||
|
||||
func sendZlp() {
|
||||
machine.SendZlp()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
@@ -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.
|
||||
)
|
||||
@@ -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
|
||||
)
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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 }
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package hid
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const bufferSize = 128
|
||||
|
||||
// RingBuffer is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
type RingBuffer struct {
|
||||
rxbuffer [bufferSize][9]byte
|
||||
head volatile.Register8
|
||||
tail volatile.Register8
|
||||
}
|
||||
|
||||
// NewRingBuffer returns a new ring buffer.
|
||||
func NewRingBuffer() *RingBuffer {
|
||||
return &RingBuffer{}
|
||||
}
|
||||
|
||||
// Used returns how many bytes in buffer have been used.
|
||||
func (rb *RingBuffer) Used() uint8 {
|
||||
return uint8(rb.head.Get() - rb.tail.Get())
|
||||
}
|
||||
|
||||
// Put stores a byte in the buffer. If the buffer is already
|
||||
// full, the method will return false.
|
||||
func (rb *RingBuffer) Put(val []byte) bool {
|
||||
if rb.Used() != bufferSize {
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
copy(rb.rxbuffer[rb.head.Get()%bufferSize][:], val)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get returns a byte from the buffer. If the buffer is empty,
|
||||
// the method will return a false as the second value.
|
||||
func (rb *RingBuffer) Get() ([]byte, bool) {
|
||||
if rb.Used() != 0 {
|
||||
rb.tail.Set(rb.tail.Get() + 1)
|
||||
return rb.rxbuffer[rb.tail.Get()%bufferSize][:], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Clear resets the head and tail pointer to zero.
|
||||
func (rb *RingBuffer) Clear() {
|
||||
rb.head.Set(0)
|
||||
rb.tail.Set(0)
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package hid
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
)
|
||||
|
||||
// from usb-hid.go
|
||||
var (
|
||||
ErrHIDInvalidPort = errors.New("invalid USB port")
|
||||
ErrHIDInvalidCore = errors.New("invalid USB core")
|
||||
ErrHIDReportTransfer = errors.New("failed to transfer HID report")
|
||||
)
|
||||
|
||||
const (
|
||||
hidEndpoint = 4
|
||||
|
||||
usb_SET_REPORT_TYPE = 33
|
||||
usb_SET_IDLE = 10
|
||||
)
|
||||
|
||||
type hidDevicer interface {
|
||||
Callback() bool
|
||||
}
|
||||
|
||||
var devices [5]hidDevicer
|
||||
var size int
|
||||
|
||||
// SetCallbackHandler sets the callback. Only the first time it is called, it
|
||||
// calls machine.EnableHID for USB configuration
|
||||
func SetCallbackHandler(d hidDevicer) {
|
||||
if size == 0 {
|
||||
machine.EnableHID(callback, nil, nil)
|
||||
}
|
||||
|
||||
devices[size] = d
|
||||
size++
|
||||
}
|
||||
|
||||
func callback() {
|
||||
for _, d := range devices {
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
if done := d.Callback(); done {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func callbackSetup(setup machine.USBSetup) bool {
|
||||
ok := false
|
||||
if setup.BmRequestType == usb_SET_REPORT_TYPE && setup.BRequest == usb_SET_IDLE {
|
||||
machine.SendZlp()
|
||||
ok = true
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// SendUSBPacket sends a HIDPacket.
|
||||
func SendUSBPacket(b []byte) {
|
||||
machine.SendUSBInPacket(hidEndpoint, b)
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
package keyboard
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine/usb/hid"
|
||||
)
|
||||
|
||||
// from usb-hid-keyboard.go
|
||||
var (
|
||||
ErrInvalidCodepoint = errors.New("invalid Unicode codepoint")
|
||||
ErrInvalidKeycode = errors.New("invalid keyboard keycode")
|
||||
ErrInvalidUTF8 = errors.New("invalid UTF-8 encoding")
|
||||
ErrKeypressMaximum = errors.New("maximum keypresses exceeded")
|
||||
)
|
||||
|
||||
var Keyboard *keyboard
|
||||
|
||||
// Keyboard represents a USB HID keyboard device with support for international
|
||||
// layouts and various control, system, multimedia, and consumer keycodes.
|
||||
//
|
||||
// Keyboard implements the io.Writer interface that translates UTF-8 encoded
|
||||
// byte strings into sequences of keypress events.
|
||||
type keyboard struct {
|
||||
// led holds the current state of all keyboard LEDs:
|
||||
// 1=NumLock 2=CapsLock 4=ScrollLock 8=Compose 16=Kana
|
||||
led uint8
|
||||
|
||||
// mod holds the current state of all keyboard modifier keys:
|
||||
// 1=LeftCtrl 2=LeftShift 4=LeftAlt 8=LeftGUI
|
||||
// 16=RightCtrl 32=RightShift 64=RightAlt 128=RightGUI
|
||||
mod uint8
|
||||
|
||||
// key holds a list of all keyboard keys currently pressed.
|
||||
key [hidKeyboardKeyCount]uint8
|
||||
con [hidKeyboardConCount]uint16
|
||||
sys [hidKeyboardSysCount]uint8
|
||||
|
||||
// decode holds the current state of the UTF-8 decoder.
|
||||
decode decodeState
|
||||
|
||||
// wideChar holds high bits for the UTF-8 decoder.
|
||||
wideChar uint16
|
||||
|
||||
buf *hid.RingBuffer
|
||||
waitTxc bool
|
||||
}
|
||||
|
||||
// decodeState represents a state in the UTF-8 decode state machine.
|
||||
type decodeState uint8
|
||||
|
||||
// Constant enumerated values of type decodeState.
|
||||
const (
|
||||
decodeReset decodeState = iota
|
||||
decodeByte1
|
||||
decodeByte2
|
||||
decodeByte3
|
||||
)
|
||||
|
||||
func init() {
|
||||
if Keyboard == nil {
|
||||
Keyboard = newKeyboard()
|
||||
hid.SetCallbackHandler(Keyboard)
|
||||
}
|
||||
}
|
||||
|
||||
// New returns hid-keybord.
|
||||
func New() *keyboard {
|
||||
return Keyboard
|
||||
}
|
||||
|
||||
func newKeyboard() *keyboard {
|
||||
return &keyboard{
|
||||
buf: hid.NewRingBuffer(),
|
||||
}
|
||||
}
|
||||
|
||||
func (kb *keyboard) Callback() bool {
|
||||
kb.waitTxc = false
|
||||
if b, ok := kb.buf.Get(); ok {
|
||||
kb.waitTxc = true
|
||||
hid.SendUSBPacket(b)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (kb *keyboard) tx(b []byte) {
|
||||
if kb.waitTxc {
|
||||
kb.buf.Put(b)
|
||||
} else {
|
||||
kb.waitTxc = true
|
||||
hid.SendUSBPacket(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (kb *keyboard) ready() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Write transmits press-and-release key sequences for each Keycode translated
|
||||
// from the given UTF-8 byte string. Write implements the io.Writer interface
|
||||
// and conforms to all documented conventions for arguments and return values.
|
||||
func (kb *keyboard) Write(b []byte) (n int, err error) {
|
||||
for _, c := range b {
|
||||
if err = kb.WriteByte(c); nil != err {
|
||||
break
|
||||
}
|
||||
n += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// WriteByte processes a single byte from a UTF-8 byte string. This method is a
|
||||
// stateful method with respect to the receiver Keyboard, meaning that its exact
|
||||
// behavior will depend on the current state of its UTF-8 decode state machine:
|
||||
//
|
||||
// (a) If the given byte is a valid ASCII encoding (0-127), then a keypress
|
||||
// sequence is immediately transmitted for the respective Keycode.
|
||||
//
|
||||
// (b) If the given byte represents the final byte in a multi-byte codepoint,
|
||||
// then a keypress sequence is immediately transmitted by translating the
|
||||
// multi-byte codepoint to its respective Keycode.
|
||||
//
|
||||
// (c) If the given byte appears to represent high bits for a multi-byte
|
||||
// codepoint, then the bits are copied to the receiver's internal state
|
||||
// machine buffer for use by a subsequent call to WriteByte() (or Write())
|
||||
// that completes the codepoint.
|
||||
//
|
||||
// (d) If the given byte is out of range, or contains illegal bits for the
|
||||
// current state of the UTF-8 decoder, then the UTF-8 decode state machine
|
||||
// is reset to its initial state.
|
||||
//
|
||||
// In cases (c) and (d), a keypress sequence is not generated and no data is
|
||||
// transmitted. In case (c), additional bytes must be received via WriteByte()
|
||||
// (or Write()) to complete or discard the current codepoint.
|
||||
func (kb *keyboard) WriteByte(b byte) error {
|
||||
switch {
|
||||
case b < 0x80:
|
||||
// 1-byte encoding (0x00-0x7F)
|
||||
kb.decode = decodeByte1
|
||||
return kb.write(uint16(b))
|
||||
|
||||
case b < 0xC0:
|
||||
// 2nd, 3rd, or 4th byte (0x80-0xBF)
|
||||
b = Keycode(b).key()
|
||||
switch kb.decode {
|
||||
case decodeByte2:
|
||||
kb.decode = decodeByte1
|
||||
return kb.write(kb.wideChar | uint16(b))
|
||||
case decodeByte3:
|
||||
kb.decode = decodeByte2
|
||||
kb.wideChar |= uint16(b) << 6
|
||||
}
|
||||
|
||||
case b < 0xE0:
|
||||
// 2-byte encoding (0xC2-0xDF), or illegal byte 2 (0xC0-0xC1)
|
||||
kb.decode = decodeByte2
|
||||
kb.wideChar = uint16(b&0x1F) << 6
|
||||
|
||||
case b < 0xF0:
|
||||
// 3-byte encoding (0xE0-0xEF)
|
||||
kb.decode = decodeByte3
|
||||
kb.wideChar = uint16(b&0x0F) << 12
|
||||
|
||||
default:
|
||||
// 4-byte encoding unsupported (0xF0-0xF4), or illegal byte 4 (0xF5-0xFF)
|
||||
kb.decode = decodeReset
|
||||
return ErrInvalidUTF8
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *keyboard) write(p uint16) error {
|
||||
c := keycode(p)
|
||||
if 0 == c {
|
||||
return ErrInvalidCodepoint
|
||||
}
|
||||
if d := deadkey(c); 0 != d {
|
||||
if err := kb.writeKeycode(d); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return kb.writeKeycode(c)
|
||||
}
|
||||
|
||||
func (kb *keyboard) writeKeycode(c Keycode) error {
|
||||
var b [9]byte
|
||||
b[0] = 0x02
|
||||
b[1] = c.mod()
|
||||
b[2] = 0
|
||||
b[3] = c.key()
|
||||
b[4] = 0
|
||||
b[5] = 0
|
||||
b[6] = 0
|
||||
b[7] = 0
|
||||
b[8] = 0
|
||||
if !kb.sendKey(false, b[:]) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
|
||||
b[1] = 0
|
||||
b[3] = 0
|
||||
if !kb.sendKey(false, b[:]) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Press transmits a press-and-release sequence for the given Keycode, which
|
||||
// simulates a discrete keypress event.
|
||||
//
|
||||
// The following values of Keycode are supported:
|
||||
//
|
||||
// 0x0020 - 0x007F ASCII (U+0020 to U+007F) [USES LAYOUT]
|
||||
// 0x0080 - 0xC1FF Unicode (U+0080 to U+C1FF) [USES LAYOUT]
|
||||
// 0xC200 - 0xDFFF UTF-8 packed (U+0080 to U+07FF) [USES LAYOUT]
|
||||
// 0xE000 - 0xE0FF Modifier key (bitmap, 8 keys, Shift/Ctrl/Alt/GUI)
|
||||
// 0xE200 - 0xE2FF System key (HID usage code, page 1)
|
||||
// 0xE400 - 0xE7FF Media/Consumer key (HID usage code, page 12)
|
||||
// 0xF000 - 0xFFFF Normal key (HID usage code, page 7)
|
||||
func (kb *keyboard) Press(c Keycode) error {
|
||||
if err := kb.Down(c); nil != err {
|
||||
return err
|
||||
}
|
||||
return kb.Up(c)
|
||||
}
|
||||
|
||||
func (kb *keyboard) sendKey(consumer bool, b []byte) bool {
|
||||
kb.tx(b)
|
||||
return true
|
||||
}
|
||||
|
||||
func (kb *keyboard) keyboardSendKeys(consumer bool) bool {
|
||||
var b [9]byte
|
||||
b[0] = 0x02
|
||||
b[1] = kb.mod
|
||||
b[2] = 0x02
|
||||
b[3] = kb.key[0]
|
||||
b[4] = kb.key[1]
|
||||
b[5] = kb.key[2]
|
||||
b[6] = kb.key[3]
|
||||
b[7] = kb.key[4]
|
||||
b[8] = kb.key[5]
|
||||
return kb.sendKey(consumer, b[:])
|
||||
}
|
||||
|
||||
// Down transmits a key-down event for the given Keycode.
|
||||
//
|
||||
// The host will interpret the key as being held down continuously until a
|
||||
// corresponding key-up event is transmitted, e.g., via method Up().
|
||||
//
|
||||
// See godoc comment on method Press() for details on what input is accepted and
|
||||
// how it is interpreted.
|
||||
func (kb *keyboard) Down(c Keycode) error {
|
||||
var res uint8
|
||||
msb := c >> 8
|
||||
if msb >= 0xC2 {
|
||||
if msb < 0xE0 {
|
||||
c = ((msb & 0x1F) << 6) | Keycode(c.key())
|
||||
} else {
|
||||
switch msb {
|
||||
case 0xF0:
|
||||
return kb.down(uint8(c), 0)
|
||||
|
||||
case 0xE0:
|
||||
return kb.down(0, uint8(c))
|
||||
|
||||
case 0xE2:
|
||||
return kb.downSys(uint8(c))
|
||||
|
||||
default:
|
||||
if 0xE4 <= msb && msb <= 0xE7 {
|
||||
return kb.downCon(uint16(c & 0x03FF))
|
||||
}
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
}
|
||||
}
|
||||
c = keycode(uint16(c))
|
||||
if 0 == c {
|
||||
return ErrInvalidCodepoint
|
||||
}
|
||||
if d := deadkey(c); 0 != d {
|
||||
res = kb.mod
|
||||
if 0 != res {
|
||||
kb.mod = 0
|
||||
kb.keyboardSendKeys(false)
|
||||
}
|
||||
kb.down(d.key(), d.mod())
|
||||
kb.up(d.key(), d.mod())
|
||||
}
|
||||
return kb.down(c.key(), c.mod()|res)
|
||||
}
|
||||
|
||||
func (kb *keyboard) down(key uint8, mod uint8) error {
|
||||
send := false
|
||||
if 0 != mod {
|
||||
if kb.mod&mod != mod {
|
||||
kb.mod |= mod
|
||||
send = true
|
||||
}
|
||||
}
|
||||
if 0 != key {
|
||||
for _, k := range kb.key {
|
||||
if k == key {
|
||||
goto end
|
||||
}
|
||||
}
|
||||
for i, k := range kb.key {
|
||||
if 0 == k {
|
||||
kb.key[i] = key
|
||||
send = true
|
||||
goto end
|
||||
}
|
||||
}
|
||||
return ErrKeypressMaximum
|
||||
}
|
||||
end:
|
||||
if send {
|
||||
if !kb.keyboardSendKeys(false) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *keyboard) downCon(key uint16) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for _, k := range kb.con {
|
||||
if key == k {
|
||||
return nil // already pressed
|
||||
}
|
||||
}
|
||||
for i, k := range kb.con {
|
||||
if 0 == k {
|
||||
kb.con[i] = key
|
||||
if !kb.keyboardSendKeys(true) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrKeypressMaximum
|
||||
}
|
||||
|
||||
func (kb *keyboard) downSys(key uint8) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for _, k := range kb.sys {
|
||||
if key == k {
|
||||
return nil // already pressed
|
||||
}
|
||||
}
|
||||
for i, k := range kb.sys {
|
||||
if 0 == k {
|
||||
kb.sys[i] = key
|
||||
if !kb.keyboardSendKeys(true) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return ErrKeypressMaximum
|
||||
}
|
||||
|
||||
// Up transmits a key-up event for the given Keycode.
|
||||
//
|
||||
// See godoc comment on method Press() for details on what input is accepted and
|
||||
// how it is interpreted.
|
||||
func (kb *keyboard) Up(c Keycode) error {
|
||||
msb := c >> 8
|
||||
if msb >= 0xC2 {
|
||||
if msb < 0xE0 {
|
||||
c = ((msb & 0x1F) << 6) | Keycode(c.key())
|
||||
} else {
|
||||
switch msb {
|
||||
case 0xF0:
|
||||
return kb.up(uint8(c), 0)
|
||||
|
||||
case 0xE0:
|
||||
return kb.up(0, uint8(c))
|
||||
|
||||
case 0xE2:
|
||||
return kb.upSys(uint8(c))
|
||||
|
||||
default:
|
||||
if 0xE4 <= msb && msb <= 0xE7 {
|
||||
return kb.upCon(uint16(c & 0x03FF))
|
||||
}
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
}
|
||||
}
|
||||
c = keycode(uint16(c))
|
||||
if 0 == c {
|
||||
return ErrInvalidCodepoint
|
||||
}
|
||||
return kb.up(c.key(), c.mod())
|
||||
}
|
||||
|
||||
// Release transmits a key-up event for all keyboard keys currently pressed as
|
||||
// if the user removed his/her hands from the keyboard entirely.
|
||||
func (kb *keyboard) Release() error {
|
||||
|
||||
bits := uint16(kb.mod)
|
||||
kb.mod = 0
|
||||
for i, k := range kb.key {
|
||||
bits |= uint16(k)
|
||||
kb.key[i] = 0
|
||||
}
|
||||
if 0 != bits {
|
||||
if !kb.keyboardSendKeys(false) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
bits = 0
|
||||
for i, k := range kb.con {
|
||||
bits |= k
|
||||
kb.con[i] = 0
|
||||
}
|
||||
for i, k := range kb.sys {
|
||||
bits |= uint16(k)
|
||||
kb.sys[i] = 0
|
||||
}
|
||||
if 0 != bits {
|
||||
if !kb.keyboardSendKeys(true) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *keyboard) up(key uint8, mod uint8) error {
|
||||
send := false
|
||||
if 0 != mod {
|
||||
if kb.mod&mod != 0 {
|
||||
kb.mod &^= mod
|
||||
send = true
|
||||
}
|
||||
}
|
||||
if 0 != key {
|
||||
for i, k := range kb.key {
|
||||
if key == k {
|
||||
kb.key[i] = 0
|
||||
send = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if send {
|
||||
if !kb.keyboardSendKeys(false) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *keyboard) upCon(key uint16) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for i, k := range kb.con {
|
||||
if key == k {
|
||||
kb.con[i] = 0
|
||||
if !kb.keyboardSendKeys(true) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kb *keyboard) upSys(key uint8) error {
|
||||
if 0 == key {
|
||||
return ErrInvalidKeycode
|
||||
}
|
||||
for i, k := range kb.sys {
|
||||
if key == k {
|
||||
kb.sys[i] = 0
|
||||
if !kb.keyboardSendKeys(true) {
|
||||
return hid.ErrHIDReportTransfer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,133 @@
|
||||
package mouse
|
||||
|
||||
import (
|
||||
"machine/usb/hid"
|
||||
)
|
||||
|
||||
var Mouse *mouse
|
||||
|
||||
type Button byte
|
||||
|
||||
const (
|
||||
Left Button = 1 << iota
|
||||
Right
|
||||
Middle
|
||||
)
|
||||
|
||||
type mouse struct {
|
||||
buf *hid.RingBuffer
|
||||
button Button
|
||||
waitTxc bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
if Mouse == nil {
|
||||
Mouse = newMouse()
|
||||
hid.SetCallbackHandler(Mouse)
|
||||
}
|
||||
}
|
||||
|
||||
// New returns hid-mouse.
|
||||
func New() *mouse {
|
||||
return Mouse
|
||||
}
|
||||
|
||||
func newMouse() *mouse {
|
||||
return &mouse{
|
||||
buf: hid.NewRingBuffer(),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mouse) Callback() bool {
|
||||
m.waitTxc = false
|
||||
if b, ok := m.buf.Get(); ok {
|
||||
m.waitTxc = true
|
||||
hid.SendUSBPacket(b[:5])
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *mouse) tx(b []byte) {
|
||||
if m.waitTxc {
|
||||
m.buf.Put(b)
|
||||
} else {
|
||||
m.waitTxc = true
|
||||
hid.SendUSBPacket(b)
|
||||
}
|
||||
}
|
||||
|
||||
// Move is a function that moves the mouse cursor.
|
||||
func (m *mouse) Move(vx, vy int) {
|
||||
if vx == 0 && vy == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if vx < -128 {
|
||||
vx = -128
|
||||
}
|
||||
if vx > 127 {
|
||||
vx = 127
|
||||
}
|
||||
|
||||
if vy < -128 {
|
||||
vy = -128
|
||||
}
|
||||
if vy > 127 {
|
||||
vy = 127
|
||||
}
|
||||
|
||||
m.tx([]byte{
|
||||
0x01, byte(m.button), byte(vx), byte(vy), 0x00,
|
||||
})
|
||||
}
|
||||
|
||||
// Cilck clicks the mouse button.
|
||||
func (m *mouse) Click(btn Button) {
|
||||
m.Press(btn)
|
||||
m.Release(btn)
|
||||
}
|
||||
|
||||
// Press presses the given mouse buttons.
|
||||
func (m *mouse) Press(btn Button) {
|
||||
m.button |= btn
|
||||
m.tx([]byte{
|
||||
0x01, byte(m.button), 0x00, 0x00, 0x00,
|
||||
})
|
||||
}
|
||||
|
||||
// Release releases the given mouse buttons.
|
||||
func (m *mouse) Release(btn Button) {
|
||||
m.button &= ^btn
|
||||
m.tx([]byte{
|
||||
0x01, byte(m.button), 0x00, 0x00, 0x00,
|
||||
})
|
||||
}
|
||||
|
||||
// Wheel controls the mouse wheel.
|
||||
func (m *mouse) Wheel(v int) {
|
||||
if v == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if v < -128 {
|
||||
v = -128
|
||||
}
|
||||
if v > 127 {
|
||||
v = 127
|
||||
}
|
||||
|
||||
m.tx([]byte{
|
||||
0x01, byte(m.button), 0x00, 0x00, byte(v),
|
||||
})
|
||||
}
|
||||
|
||||
// WheelDown turns the mouse wheel down.
|
||||
func (m *mouse) WheelDown() {
|
||||
m.Wheel(-1)
|
||||
}
|
||||
|
||||
// WheelUp turns the mouse wheel up.
|
||||
func (m *mouse) WheelUp() {
|
||||
m.Wheel(1)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package midi
|
||||
|
||||
import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const bufferSize = 128
|
||||
|
||||
// RingBuffer is ring buffer implementation inspired by post at
|
||||
// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php
|
||||
type RingBuffer struct {
|
||||
rxbuffer [bufferSize][4]byte
|
||||
head volatile.Register8
|
||||
tail volatile.Register8
|
||||
}
|
||||
|
||||
// NewRingBuffer returns a new ring buffer.
|
||||
func NewRingBuffer() *RingBuffer {
|
||||
return &RingBuffer{}
|
||||
}
|
||||
|
||||
// Used returns how many bytes in buffer have been used.
|
||||
func (rb *RingBuffer) Used() uint8 {
|
||||
return uint8(rb.head.Get() - rb.tail.Get())
|
||||
}
|
||||
|
||||
// Put stores a byte in the buffer. If the buffer is already
|
||||
// full, the method will return false.
|
||||
func (rb *RingBuffer) Put(val []byte) bool {
|
||||
if rb.Used() != bufferSize {
|
||||
rb.head.Set(rb.head.Get() + 1)
|
||||
copy(rb.rxbuffer[rb.head.Get()%bufferSize][:], val)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get returns a byte from the buffer. If the buffer is empty,
|
||||
// the method will return a false as the second value.
|
||||
func (rb *RingBuffer) Get() ([]byte, bool) {
|
||||
if rb.Used() != 0 {
|
||||
rb.tail.Set(rb.tail.Get() + 1)
|
||||
return rb.rxbuffer[rb.tail.Get()%bufferSize][:], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Clear resets the head and tail pointer to zero.
|
||||
func (rb *RingBuffer) Clear() {
|
||||
rb.head.Set(0)
|
||||
rb.tail.Set(0)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package midi
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
const (
|
||||
midiEndpointOut = 5 // from PC
|
||||
midiEndpointIn = 6 // to PC
|
||||
)
|
||||
|
||||
var Midi *midi
|
||||
|
||||
type midi struct {
|
||||
buf *RingBuffer
|
||||
callbackFuncRx func([]byte)
|
||||
waitTxc bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
if Midi == nil {
|
||||
Midi = newMidi()
|
||||
}
|
||||
}
|
||||
|
||||
// New returns hid-mouse.
|
||||
func New() *midi {
|
||||
return Midi
|
||||
}
|
||||
|
||||
func newMidi() *midi {
|
||||
m := &midi{
|
||||
buf: NewRingBuffer(),
|
||||
}
|
||||
machine.EnableMIDI(m.Callback, m.CallbackRx, nil)
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *midi) SetCallback(callbackRx func([]byte)) {
|
||||
m.callbackFuncRx = callbackRx
|
||||
}
|
||||
|
||||
func (m *midi) Write(b []byte) (n int, err error) {
|
||||
i := 0
|
||||
for i = 0; i < len(b); i += 4 {
|
||||
m.tx(b[i : i+4])
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// sendUSBPacket sends a MIDIPacket.
|
||||
func (m *midi) sendUSBPacket(b []byte) {
|
||||
machine.SendUSBInPacket(midiEndpointIn, b)
|
||||
}
|
||||
|
||||
// from BulkIn
|
||||
func (m *midi) Callback() {
|
||||
m.waitTxc = false
|
||||
if b, ok := m.buf.Get(); ok {
|
||||
m.waitTxc = true
|
||||
m.sendUSBPacket(b)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *midi) tx(b []byte) {
|
||||
if m.waitTxc {
|
||||
m.buf.Put(b)
|
||||
} else {
|
||||
m.waitTxc = true
|
||||
m.sendUSBPacket(b)
|
||||
}
|
||||
}
|
||||
|
||||
// from BulkOut
|
||||
func (m *midi) CallbackRx(b []byte) {
|
||||
if m.callbackFuncRx != nil {
|
||||
m.callbackFuncRx(b)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//go:build sam || nrf52840 || rp2040
|
||||
// +build sam nrf52840 rp2040
|
||||
|
||||
package machine
|
||||
|
||||
type USBDescriptor struct {
|
||||
Device []byte
|
||||
Configuration []byte
|
||||
HID map[uint16][]byte
|
||||
}
|
||||
|
||||
func (d *USBDescriptor) Configure(idVendor, idProduct uint16) {
|
||||
d.Device[8] = byte(idVendor)
|
||||
d.Device[9] = byte(idVendor >> 8)
|
||||
d.Device[10] = byte(idProduct)
|
||||
d.Device[11] = byte(idProduct >> 8)
|
||||
|
||||
d.Configuration[2] = byte(len(d.Configuration))
|
||||
d.Configuration[3] = byte(len(d.Configuration) >> 8)
|
||||
}
|
||||
|
||||
var descriptorCDC = USBDescriptor{
|
||||
Device: []byte{
|
||||
0x12, 0x01, 0x00, 0x02, 0xef, 0x02, 0x01, 0x40, 0x86, 0x28, 0x2d, 0x80, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01,
|
||||
},
|
||||
Configuration: []byte{
|
||||
0x09, 0x02, 0x4b, 0x00, 0x02, 0x01, 0x00, 0xa0, 0x32,
|
||||
0x08, 0x0b, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,
|
||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00,
|
||||
0x05, 0x24, 0x00, 0x10, 0x01,
|
||||
0x04, 0x24, 0x02, 0x06,
|
||||
0x05, 0x24, 0x06, 0x00, 0x01,
|
||||
0x05, 0x24, 0x01, 0x01, 0x01,
|
||||
0x07, 0x05, 0x81, 0x03, 0x10, 0x00, 0x10,
|
||||
0x09, 0x04, 0x01, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
|
||||
0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00,
|
||||
},
|
||||
}
|
||||
|
||||
var descriptorCDCHID = USBDescriptor{
|
||||
Device: []byte{
|
||||
0x12, 0x01, 0x00, 0x02, 0xef, 0x02, 0x01, 0x40, 0x86, 0x28, 0x2d, 0x80, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01,
|
||||
},
|
||||
Configuration: []byte{
|
||||
0x09, 0x02, 0x64, 0x00, 0x03, 0x01, 0x00, 0xa0, 0x32,
|
||||
0x08, 0x0b, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,
|
||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00,
|
||||
0x05, 0x24, 0x00, 0x10, 0x01,
|
||||
0x04, 0x24, 0x02, 0x06,
|
||||
0x05, 0x24, 0x06, 0x00, 0x01,
|
||||
0x05, 0x24, 0x01, 0x01, 0x01,
|
||||
0x07, 0x05, 0x81, 0x03, 0x10, 0x00, 0x10,
|
||||
0x09, 0x04, 0x01, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
|
||||
0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00,
|
||||
0x09, 0x04, 0x02, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00,
|
||||
0x09, 0x21, 0x01, 0x01, 0x00, 0x01, 0x22, 0x65, 0x00,
|
||||
0x07, 0x05, 0x84, 0x03, 0x40, 0x00, 0x01,
|
||||
},
|
||||
HID: map[uint16][]byte{
|
||||
2: []byte{
|
||||
// keyboard and mouse
|
||||
0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x85, 0x02, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00,
|
||||
0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x03, 0x95, 0x06,
|
||||
0x75, 0x08, 0x15, 0x00, 0x25, 0x73, 0x05, 0x07, 0x19, 0x00, 0x29, 0x73, 0x81, 0x00, 0xc0, 0x05,
|
||||
0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x85, 0x01, 0x05, 0x09, 0x19, 0x01, 0x29,
|
||||
0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81,
|
||||
0x03, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95,
|
||||
0x03, 0x81, 0x06, 0xc0, 0xc0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var descriptorCDCMIDI = USBDescriptor{
|
||||
Device: []byte{
|
||||
0x12, 0x01, 0x00, 0x02, 0xef, 0x02, 0x01, 0x40, 0x86, 0x28, 0x2d, 0x80, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01,
|
||||
},
|
||||
Configuration: []byte{
|
||||
0x09, 0x02, 0xaf, 0x00, 0x04, 0x01, 0x00, 0xa0, 0x32,
|
||||
0x08, 0x0b, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,
|
||||
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00,
|
||||
0x05, 0x24, 0x00, 0x10, 0x01,
|
||||
0x04, 0x24, 0x02, 0x06,
|
||||
0x05, 0x24, 0x06, 0x00, 0x01,
|
||||
0x05, 0x24, 0x01, 0x01, 0x01,
|
||||
0x07, 0x05, 0x81, 0x03, 0x10, 0x00, 0x10,
|
||||
0x09, 0x04, 0x01, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00,
|
||||
0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00,
|
||||
0x08, 0x0b, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00,
|
||||
0x09, 0x04, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
|
||||
0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x03,
|
||||
0x09, 0x04, 0x03, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00,
|
||||
0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00,
|
||||
0x06, 0x24, 0x02, 0x01, 0x01, 0x00,
|
||||
0x06, 0x24, 0x02, 0x02, 0x02, 0x00,
|
||||
0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00,
|
||||
0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x01, 0x01, 0x00,
|
||||
0x09, 0x05, 0x05, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||
0x05, 0x25, 0x01, 0x01, 0x01,
|
||||
0x09, 0x05, 0x86, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00,
|
||||
0x05, 0x25, 0x01, 0x01, 0x03,
|
||||
},
|
||||
}
|
||||
+28
-2
@@ -38,9 +38,26 @@ func NewFile(fd uintptr, name string) *File {
|
||||
return &File{&file{stdioFileHandle(fd), name}}
|
||||
}
|
||||
|
||||
// Read is unsupported on this system.
|
||||
// Read reads up to len(b) bytes from machine.Serial.
|
||||
// It returns the number of bytes read and any error encountered.
|
||||
func (f stdioFileHandle) Read(b []byte) (n int, err error) {
|
||||
return 0, ErrUnsupported
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
size := buffered()
|
||||
for size == 0 {
|
||||
gosched()
|
||||
size = buffered()
|
||||
}
|
||||
|
||||
if size > len(b) {
|
||||
size = len(b)
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
b[i] = getchar()
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
func (f stdioFileHandle) ReadAt(b []byte, off int64) (n int, err error) {
|
||||
@@ -78,6 +95,15 @@ func (f stdioFileHandle) Fd() uintptr {
|
||||
//go:linkname putchar runtime.putchar
|
||||
func putchar(c byte)
|
||||
|
||||
//go:linkname getchar runtime.getchar
|
||||
func getchar() byte
|
||||
|
||||
//go:linkname buffered runtime.buffered
|
||||
func buffered() int
|
||||
|
||||
//go:linkname gosched runtime.Gosched
|
||||
func gosched() int
|
||||
|
||||
func Pipe() (r *File, w *File, err error) {
|
||||
return nil, nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
//go:build windows || darwin || (linux && !baremetal)
|
||||
// +build windows darwin linux,!baremetal
|
||||
|
||||
package os_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetpagesize(t *testing.T) {
|
||||
pagesize := os.Getpagesize()
|
||||
if pagesize == 0x1000 || pagesize == 0x4000 || pagesize == 0x10000 {
|
||||
return
|
||||
}
|
||||
t.Errorf("os.Getpagesize() returns strange value %d", pagesize)
|
||||
}
|
||||
@@ -7,6 +7,11 @@
|
||||
|
||||
package os
|
||||
|
||||
import "syscall"
|
||||
|
||||
// Getpagesize returns the underlying system's memory page size.
|
||||
func Getpagesize() int { return syscall.Getpagesize() }
|
||||
|
||||
func (fs *fileStat) Name() string { return fs.name }
|
||||
func (fs *fileStat) IsDir() bool { return fs.Mode().IsDir() }
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]struct{}) bool {
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
if v1.UnsafePointer() == v2.UnsafePointer() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
@@ -91,7 +91,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]struct{}) bool {
|
||||
}
|
||||
return deepValueEqual(v1.Elem(), v2.Elem(), visited)
|
||||
case Ptr:
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
if v1.UnsafePointer() == v2.UnsafePointer() {
|
||||
return true
|
||||
}
|
||||
return deepValueEqual(v1.Elem(), v2.Elem(), visited)
|
||||
@@ -109,7 +109,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]struct{}) bool {
|
||||
if v1.Len() != v2.Len() {
|
||||
return false
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
if v1.UnsafePointer() == v2.UnsafePointer() {
|
||||
return true
|
||||
}
|
||||
for _, k := range v1.MapKeys() {
|
||||
|
||||
+10
-4
@@ -135,16 +135,22 @@ func (v Value) IsNil() bool {
|
||||
// Pointer returns the underlying pointer of the given value for the following
|
||||
// types: chan, map, pointer, unsafe.Pointer, slice, func.
|
||||
func (v Value) Pointer() uintptr {
|
||||
return uintptr(v.UnsafePointer())
|
||||
}
|
||||
|
||||
// UnsafePointer returns the underlying pointer of the given value for the
|
||||
// following types: chan, map, pointer, unsafe.Pointer, slice, func.
|
||||
func (v Value) UnsafePointer() unsafe.Pointer {
|
||||
switch v.Kind() {
|
||||
case Chan, Map, Ptr, UnsafePointer:
|
||||
return uintptr(v.pointer())
|
||||
return v.pointer()
|
||||
case Slice:
|
||||
slice := (*sliceHeader)(v.value)
|
||||
return uintptr(slice.data)
|
||||
return slice.data
|
||||
case Func:
|
||||
panic("unimplemented: (reflect.Value).Pointer()")
|
||||
panic("unimplemented: (reflect.Value).UnsafePointer()")
|
||||
default: // not implemented: Func
|
||||
panic(&ValueError{Method: "Pointer"})
|
||||
panic(&ValueError{Method: "UnsafePointer"})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,16 +26,16 @@ func xorshift32(x uint32) uint32 {
|
||||
// This function is used by hash/maphash.
|
||||
func memhash(p unsafe.Pointer, seed, s uintptr) uintptr {
|
||||
if unsafe.Sizeof(uintptr(0)) > 4 {
|
||||
return seed ^ uintptr(hash64(p, s))
|
||||
return uintptr(hash64(p, s, seed))
|
||||
}
|
||||
return seed ^ uintptr(hash32(p, s))
|
||||
return uintptr(hash32(p, s, seed))
|
||||
}
|
||||
|
||||
// Get FNV-1a hash of the given memory buffer.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
|
||||
func hash32(ptr unsafe.Pointer, n uintptr) uint32 {
|
||||
var result uint32 = 2166136261 // FNV offset basis
|
||||
func hash32(ptr unsafe.Pointer, n uintptr, seed uintptr) uint32 {
|
||||
var result uint32 = 2166136261 ^ uint32(seed) // FNV offset basis
|
||||
for i := uintptr(0); i < n; i++ {
|
||||
c := *(*uint8)(unsafe.Pointer(uintptr(ptr) + i))
|
||||
result ^= uint32(c) // XOR with byte
|
||||
@@ -45,8 +45,8 @@ func hash32(ptr unsafe.Pointer, n uintptr) uint32 {
|
||||
}
|
||||
|
||||
// Also a FNV-1a hash.
|
||||
func hash64(ptr unsafe.Pointer, n uintptr) uint64 {
|
||||
var result uint64 = 14695981039346656037 // FNV offset basis
|
||||
func hash64(ptr unsafe.Pointer, n uintptr, seed uintptr) uint64 {
|
||||
var result uint64 = 14695981039346656037 ^ uint64(seed) // FNV offset basis
|
||||
for i := uintptr(0); i < n; i++ {
|
||||
c := *(*uint8)(unsafe.Pointer(uintptr(ptr) + i))
|
||||
result ^= uint64(c) // XOR with byte
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
package runtime
|
||||
|
||||
// This file implements markGlobals for all the files that don't have a more
|
||||
// specific implementation.
|
||||
|
||||
// markGlobals marks all globals, which are reachable by definition.
|
||||
//
|
||||
// This implementation marks all globals conservatively and assumes it can use
|
||||
@@ -1,35 +0,0 @@
|
||||
//go:build gc.conservative && !baremetal && !darwin && !nintendoswitch && !tinygo.wasm && !windows
|
||||
// +build gc.conservative,!baremetal,!darwin,!nintendoswitch,!tinygo.wasm,!windows
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:extern runtime.trackedGlobalsStart
|
||||
var trackedGlobalsStart uintptr
|
||||
|
||||
//go:extern runtime.trackedGlobalsLength
|
||||
var trackedGlobalsLength uintptr
|
||||
|
||||
//go:extern runtime.trackedGlobalsBitmap
|
||||
var trackedGlobalsBitmap [0]uint8
|
||||
|
||||
// markGlobals marks all globals, which are reachable by definition.
|
||||
//
|
||||
// This implementation relies on a compiler pass that stores all globals in a
|
||||
// single global (adjusting all uses of them accordingly) and creates a bit
|
||||
// vector with the locations of each pointer. This implementation then walks the
|
||||
// bit vector and for each pointer it indicates, it marks the root.
|
||||
//
|
||||
//go:nobounds
|
||||
func markGlobals() {
|
||||
for i := uintptr(0); i < trackedGlobalsLength; i++ {
|
||||
if trackedGlobalsBitmap[i/8]&(1<<(i%8)) != 0 {
|
||||
addr := trackedGlobalsStart + i*unsafe.Alignof(uintptr(0))
|
||||
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||
markRoot(addr, root)
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
-39
@@ -13,12 +13,13 @@ import (
|
||||
// The underlying hashmap structure for Go.
|
||||
type hashmap struct {
|
||||
buckets unsafe.Pointer // pointer to array of buckets
|
||||
seed uintptr
|
||||
count uintptr
|
||||
keySize uint8 // maybe this can store the key type as well? E.g. keysize == 5 means string?
|
||||
valueSize uint8
|
||||
bucketBits uint8
|
||||
keyEqual func(x, y unsafe.Pointer, n uintptr) bool
|
||||
keyHash func(key unsafe.Pointer, size uintptr) uint32
|
||||
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32
|
||||
}
|
||||
|
||||
type hashmapAlgorithm uint8
|
||||
@@ -74,6 +75,7 @@ func hashmapMake(keySize, valueSize uint8, sizeHint uintptr, alg uint8) *hashmap
|
||||
|
||||
return &hashmap{
|
||||
buckets: buckets,
|
||||
seed: uintptr(fastrand()),
|
||||
keySize: keySize,
|
||||
valueSize: valueSize,
|
||||
bucketBits: bucketBits,
|
||||
@@ -96,7 +98,7 @@ func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintpt
|
||||
}
|
||||
}
|
||||
|
||||
func hashmapKeyHashAlg(alg hashmapAlgorithm) func(key unsafe.Pointer, n uintptr) uint32 {
|
||||
func hashmapKeyHashAlg(alg hashmapAlgorithm) func(key unsafe.Pointer, n, seed uintptr) uint32 {
|
||||
switch alg {
|
||||
case hashmapAlgorithmBinary:
|
||||
return hash32
|
||||
@@ -148,12 +150,14 @@ func hashmapLenUnsafePointer(p unsafe.Pointer) int {
|
||||
// Set a specified key to a given value. Grow the map if necessary.
|
||||
//go:nobounds
|
||||
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32) {
|
||||
tophash := hashmapTopHash(hash)
|
||||
|
||||
if hashmapShouldGrow(m) {
|
||||
hashmapGrow(m)
|
||||
// seed changed when we grew; rehash key with new seed
|
||||
hash = m.keyHash(key, uintptr(m.keySize), m.seed)
|
||||
}
|
||||
|
||||
tophash := hashmapTopHash(hash)
|
||||
|
||||
numBuckets := uintptr(1) << m.bucketBits
|
||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||
@@ -221,10 +225,10 @@ func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash u
|
||||
}
|
||||
|
||||
func hashmapGrow(m *hashmap) {
|
||||
|
||||
// clone map as empty
|
||||
n := *m
|
||||
n.count = 0
|
||||
n.seed = uintptr(fastrand())
|
||||
|
||||
// allocate our new buckets twice as big
|
||||
n.bucketBits = m.bucketBits + 1
|
||||
@@ -239,7 +243,7 @@ func hashmapGrow(m *hashmap) {
|
||||
var value = alloc(uintptr(m.valueSize), nil)
|
||||
|
||||
for hashmapNext(m, &it, key, value) {
|
||||
h := m.keyHash(key, uintptr(m.keySize))
|
||||
h := n.keyHash(key, uintptr(n.keySize), n.seed)
|
||||
hashmapSet(&n, key, value, h)
|
||||
}
|
||||
|
||||
@@ -386,7 +390,7 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
|
||||
|
||||
// Our view of the buckets doesn't match the parent map.
|
||||
// Look up the key in the new buckets and return that value if it exists
|
||||
hash := m.keyHash(key, uintptr(m.keySize))
|
||||
hash := m.keyHash(key, uintptr(m.keySize), m.seed)
|
||||
ok := hashmapGet(m, key, value, uintptr(m.valueSize), hash)
|
||||
if !ok {
|
||||
// doesn't exist in parent map; try next key
|
||||
@@ -401,10 +405,11 @@ func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) boo
|
||||
}
|
||||
|
||||
// Hashmap with plain binary data keys (not containing strings etc.).
|
||||
|
||||
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
||||
// TODO: detect nil map here and throw a better panic message?
|
||||
hash := hash32(key, uintptr(m.keySize))
|
||||
if m == nil {
|
||||
nilMapPanic()
|
||||
}
|
||||
hash := hash32(key, uintptr(m.keySize), m.seed)
|
||||
hashmapSet(m, key, value, hash)
|
||||
}
|
||||
|
||||
@@ -413,7 +418,7 @@ func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr)
|
||||
memzero(value, uintptr(valueSize))
|
||||
return false
|
||||
}
|
||||
hash := hash32(key, uintptr(m.keySize))
|
||||
hash := hash32(key, uintptr(m.keySize), m.seed)
|
||||
return hashmapGet(m, key, value, valueSize, hash)
|
||||
}
|
||||
|
||||
@@ -421,7 +426,7 @@ func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
hash := hash32(key, uintptr(m.keySize))
|
||||
hash := hash32(key, uintptr(m.keySize), m.seed)
|
||||
hashmapDelete(m, key, hash)
|
||||
}
|
||||
|
||||
@@ -431,28 +436,38 @@ func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||
return *(*string)(x) == *(*string)(y)
|
||||
}
|
||||
|
||||
func hashmapStringHash(s string) uint32 {
|
||||
func hashmapStringHash(s string, seed uintptr) uint32 {
|
||||
_s := (*_string)(unsafe.Pointer(&s))
|
||||
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length))
|
||||
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length), seed)
|
||||
}
|
||||
|
||||
func hashmapStringPtrHash(sptr unsafe.Pointer, size uintptr) uint32 {
|
||||
func hashmapStringPtrHash(sptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
|
||||
_s := *(*_string)(sptr)
|
||||
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length))
|
||||
return hash32(unsafe.Pointer(_s.ptr), uintptr(_s.length), seed)
|
||||
}
|
||||
|
||||
func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||
hash := hashmapStringHash(key)
|
||||
if m == nil {
|
||||
nilMapPanic()
|
||||
}
|
||||
hash := hashmapStringHash(key, m.seed)
|
||||
hashmapSet(m, unsafe.Pointer(&key), value, hash)
|
||||
}
|
||||
|
||||
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool {
|
||||
hash := hashmapStringHash(key)
|
||||
if m == nil {
|
||||
memzero(value, uintptr(valueSize))
|
||||
return false
|
||||
}
|
||||
hash := hashmapStringHash(key, m.seed)
|
||||
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
|
||||
}
|
||||
|
||||
func hashmapStringDelete(m *hashmap, key string) {
|
||||
hash := hashmapStringHash(key)
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
hash := hashmapStringHash(key, m.seed)
|
||||
hashmapDelete(m, unsafe.Pointer(&key), hash)
|
||||
}
|
||||
|
||||
@@ -465,25 +480,25 @@ func hashmapStringDelete(m *hashmap, key string) {
|
||||
//go:linkname valueInterfaceUnsafe reflect.valueInterfaceUnsafe
|
||||
func valueInterfaceUnsafe(v reflect.Value) interface{}
|
||||
|
||||
func hashmapFloat32Hash(ptr unsafe.Pointer) uint32 {
|
||||
func hashmapFloat32Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
|
||||
f := *(*uint32)(ptr)
|
||||
if f == 0x80000000 {
|
||||
// convert -0 to 0 for hashing
|
||||
f = 0
|
||||
}
|
||||
return hash32(unsafe.Pointer(&f), 4)
|
||||
return hash32(unsafe.Pointer(&f), 4, seed)
|
||||
}
|
||||
|
||||
func hashmapFloat64Hash(ptr unsafe.Pointer) uint32 {
|
||||
func hashmapFloat64Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
|
||||
f := *(*uint64)(ptr)
|
||||
if f == 0x8000000000000000 {
|
||||
// convert -0 to 0 for hashing
|
||||
f = 0
|
||||
}
|
||||
return hash32(unsafe.Pointer(&f), 8)
|
||||
return hash32(unsafe.Pointer(&f), 8, seed)
|
||||
}
|
||||
|
||||
func hashmapInterfaceHash(itf interface{}) uint32 {
|
||||
func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
|
||||
x := reflect.ValueOf(itf)
|
||||
if x.RawType() == 0 {
|
||||
return 0 // nil interface
|
||||
@@ -498,41 +513,41 @@ func hashmapInterfaceHash(itf interface{}) uint32 {
|
||||
|
||||
switch x.RawType().Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return hash32(ptr, x.RawType().Size())
|
||||
return hash32(ptr, x.RawType().Size(), seed)
|
||||
case reflect.Bool, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return hash32(ptr, x.RawType().Size())
|
||||
return hash32(ptr, x.RawType().Size(), seed)
|
||||
case reflect.Float32:
|
||||
// It should be possible to just has the contents. However, NaN != NaN
|
||||
// so if you're using lots of NaNs as map keys (you shouldn't) then hash
|
||||
// time may become exponential. To fix that, it would be better to
|
||||
// return a random number instead:
|
||||
// https://research.swtch.com/randhash
|
||||
return hashmapFloat32Hash(ptr)
|
||||
return hashmapFloat32Hash(ptr, seed)
|
||||
case reflect.Float64:
|
||||
return hashmapFloat64Hash(ptr)
|
||||
return hashmapFloat64Hash(ptr, seed)
|
||||
case reflect.Complex64:
|
||||
rptr, iptr := ptr, unsafe.Pointer(uintptr(ptr)+4)
|
||||
return hashmapFloat32Hash(rptr) ^ hashmapFloat32Hash(iptr)
|
||||
return hashmapFloat32Hash(rptr, seed) ^ hashmapFloat32Hash(iptr, seed)
|
||||
case reflect.Complex128:
|
||||
rptr, iptr := ptr, unsafe.Pointer(uintptr(ptr)+8)
|
||||
return hashmapFloat64Hash(rptr) ^ hashmapFloat64Hash(iptr)
|
||||
return hashmapFloat64Hash(rptr, seed) ^ hashmapFloat64Hash(iptr, seed)
|
||||
case reflect.String:
|
||||
return hashmapStringHash(x.String())
|
||||
return hashmapStringHash(x.String(), seed)
|
||||
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
|
||||
// It might seem better to just return the pointer, but that won't
|
||||
// result in an evenly distributed hashmap. Instead, hash the pointer
|
||||
// like most other types.
|
||||
return hash32(ptr, x.RawType().Size())
|
||||
return hash32(ptr, x.RawType().Size(), seed)
|
||||
case reflect.Array:
|
||||
var hash uint32
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)))
|
||||
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)), seed)
|
||||
}
|
||||
return hash
|
||||
case reflect.Struct:
|
||||
var hash uint32
|
||||
for i := 0; i < x.NumField(); i++ {
|
||||
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)))
|
||||
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)), seed)
|
||||
}
|
||||
return hash
|
||||
default:
|
||||
@@ -541,9 +556,9 @@ func hashmapInterfaceHash(itf interface{}) uint32 {
|
||||
}
|
||||
}
|
||||
|
||||
func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr) uint32 {
|
||||
func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
|
||||
_i := *(*_interface)(iptr)
|
||||
return hashmapInterfaceHash(_i)
|
||||
return hashmapInterfaceHash(_i, seed)
|
||||
}
|
||||
|
||||
func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||
@@ -551,16 +566,26 @@ func hashmapInterfaceEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||
}
|
||||
|
||||
func hashmapInterfaceSet(m *hashmap, key interface{}, value unsafe.Pointer) {
|
||||
hash := hashmapInterfaceHash(key)
|
||||
if m == nil {
|
||||
nilMapPanic()
|
||||
}
|
||||
hash := hashmapInterfaceHash(key, m.seed)
|
||||
hashmapSet(m, unsafe.Pointer(&key), value, hash)
|
||||
}
|
||||
|
||||
func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer, valueSize uintptr) bool {
|
||||
hash := hashmapInterfaceHash(key)
|
||||
if m == nil {
|
||||
memzero(value, uintptr(valueSize))
|
||||
return false
|
||||
}
|
||||
hash := hashmapInterfaceHash(key, m.seed)
|
||||
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash)
|
||||
}
|
||||
|
||||
func hashmapInterfaceDelete(m *hashmap, key interface{}) {
|
||||
hash := hashmapInterfaceHash(key)
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
hash := hashmapInterfaceHash(key, m.seed)
|
||||
hashmapDelete(m, unsafe.Pointer(&key), hash)
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func reflectValueEqual(x, y reflect.Value) bool {
|
||||
case reflect.String:
|
||||
return x.String() == y.String()
|
||||
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
|
||||
return x.Pointer() == y.Pointer()
|
||||
return x.UnsafePointer() == y.UnsafePointer()
|
||||
case reflect.Array:
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
if !reflectValueEqual(x.Index(i), y.Index(i)) {
|
||||
|
||||
+32
-2
@@ -1,8 +1,13 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
//go:build linux && !baremetal && !nintendoswitch && !wasi
|
||||
// +build linux,!baremetal,!nintendoswitch,!wasi
|
||||
|
||||
package runtime
|
||||
|
||||
// This file is for systems that are _actually_ Linux (not systems that pretend
|
||||
// to be Linux, like baremetal systems).
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const GOOS = "linux"
|
||||
|
||||
const (
|
||||
@@ -18,3 +23,28 @@ const (
|
||||
clock_REALTIME = 0
|
||||
clock_MONOTONIC_RAW = 4
|
||||
)
|
||||
|
||||
//go:extern _edata
|
||||
var globalsStartSymbol [0]byte
|
||||
|
||||
//go:extern _end
|
||||
var globalsEndSymbol [0]byte
|
||||
|
||||
// markGlobals marks all globals, which are reachable by definition.
|
||||
//
|
||||
// This implementation marks all globals conservatively and assumes it can use
|
||||
// linker-defined symbols for the start and end of the .data section.
|
||||
func markGlobals() {
|
||||
start := uintptr(unsafe.Pointer(&globalsStartSymbol))
|
||||
end := uintptr(unsafe.Pointer(&globalsEndSymbol))
|
||||
start = (start + unsafe.Alignof(uintptr(0)) - 1) &^ (unsafe.Alignof(uintptr(0)) - 1) // align on word boundary
|
||||
markRoots(start, end)
|
||||
}
|
||||
|
||||
//export getpagesize
|
||||
func libc_getpagesize() int
|
||||
|
||||
//go:linkname syscall_Getpagesize syscall.Getpagesize
|
||||
func syscall_Getpagesize() int {
|
||||
return libc_getpagesize()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
//go:build linux && (baremetal || nintendoswitch || wasi)
|
||||
// +build linux
|
||||
// +build baremetal nintendoswitch wasi
|
||||
|
||||
// Other systems that aren't operating systems supported by the Go toolchain
|
||||
// need to pretend to be an existing operating system. Linux seems like a good
|
||||
// choice for this for its wide hardware support.
|
||||
|
||||
package runtime
|
||||
|
||||
const GOOS = "linux"
|
||||
@@ -90,3 +90,26 @@ func markGlobals() {
|
||||
section = (*peSection)(unsafe.Pointer(uintptr(unsafe.Pointer(section)) + unsafe.Sizeof(peSection{})))
|
||||
}
|
||||
}
|
||||
|
||||
type systeminfo struct {
|
||||
anon0 [4]byte
|
||||
dwpagesize uint32
|
||||
lpminimumapplicationaddress *byte
|
||||
lpmaximumapplicationaddress *byte
|
||||
dwactiveprocessormask uintptr
|
||||
dwnumberofprocessors uint32
|
||||
dwprocessortype uint32
|
||||
dwallocationgranularity uint32
|
||||
wprocessorlevel uint16
|
||||
wprocessorrevision uint16
|
||||
}
|
||||
|
||||
//export GetSystemInfo
|
||||
func _GetSystemInfo(lpSystemInfo unsafe.Pointer)
|
||||
|
||||
//go:linkname syscall_Getpagesize syscall.Getpagesize
|
||||
func syscall_Getpagesize() int {
|
||||
var info systeminfo
|
||||
_GetSystemInfo(unsafe.Pointer(&info))
|
||||
return int(info.dwpagesize)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ func nilPanic() {
|
||||
runtimePanic("nil pointer dereference")
|
||||
}
|
||||
|
||||
// Panic when trying to add an entry to a nil map
|
||||
func nilMapPanic() {
|
||||
runtimePanic("assignment to entry in nil map")
|
||||
}
|
||||
|
||||
// Panic when trying to acces an array or slice out of bounds.
|
||||
func lookupPanic() {
|
||||
runtimePanic("index out of range")
|
||||
|
||||
@@ -14,6 +14,16 @@ func putchar(c byte) {
|
||||
// dummy, TODO
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
// dummy, TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
// dummy, TODO
|
||||
return 0
|
||||
}
|
||||
|
||||
//go:extern _sbss
|
||||
var _sbss [0]byte
|
||||
|
||||
|
||||
@@ -16,6 +16,18 @@ func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
// Sleep for a given period. The period is defined by the WDT peripheral, and is
|
||||
// on most chips (at least) 3 bits wide, in powers of two from 16ms to 2s
|
||||
// (0=16ms, 1=32ms, 2=64ms...). Note that the WDT is not very accurate: it can
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"machine"
|
||||
"machine/usb/cdc"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
@@ -29,16 +30,27 @@ func init() {
|
||||
initADCClock()
|
||||
|
||||
// connect to USB CDC interface
|
||||
cdc.EnableUSBCDC()
|
||||
machine.USB.Configure(machine.UARTConfig{})
|
||||
machine.Serial.Configure(machine.UARTConfig{})
|
||||
if !machine.USB.Configured() {
|
||||
machine.USB.Configure(machine.UARTConfig{})
|
||||
}
|
||||
}
|
||||
|
||||
func putchar(c byte) {
|
||||
machine.Serial.WriteByte(c)
|
||||
}
|
||||
|
||||
func getchar() byte {
|
||||
for machine.Serial.Buffered() == 0 {
|
||||
Gosched()
|
||||
}
|
||||
v, _ := machine.Serial.ReadByte()
|
||||
return v
|
||||
}
|
||||
|
||||
func buffered() int {
|
||||
return machine.Serial.Buffered()
|
||||
}
|
||||
|
||||
func initClocks() {
|
||||
// Set 1 Flash Wait State for 48MHz, required for 3.3V operation according to SAMD21 Datasheet
|
||||
sam.NVMCTRL.CTRLB.SetBits(sam.NVMCTRL_CTRLB_RWS_HALF << sam.NVMCTRL_CTRLB_RWS_Pos)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"machine"
|
||||
"machine/usb/cdc"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user