Compare commits

..

1 Commits

Author SHA1 Message Date
sago35 2a56d5e216 machine: add USBDevice.Attach and USBDevice.Detach
The USB device is attached to the bus automatically during startup,
before user code has a chance to finish its USB configuration (device
identifiers, extra HID interfaces, ...). Composite devices such as
keyboards may therefore be enumerated by the host with an incomplete
configuration.

Attach and Detach expose the soft-connect control (DP pull-up) so that
an application or library can detach in an init function, complete its
configuration, and attach again to let the host enumerate the finished
device. They can also be used to force re-enumeration without
replugging the cable.

Implemented for atsamd21, atsamd51, nrf52840, rp2040 and rp2350.
2026-07-28 22:03:38 +09:00
17 changed files with 101 additions and 41 deletions
+2 -2
View File
@@ -40,7 +40,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.27.0-rc.3'
go-version: '1.27.0-rc.2'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v5
@@ -131,7 +131,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.27.0-rc.3'
go-version: '1.27.0-rc.2'
cache: true
- name: Build TinyGo (LLVM ${{ matrix.version }})
run: go install -tags=llvm${{ matrix.version }}
+5 -5
View File
@@ -23,7 +23,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.27.0-rc.3'
go-version: '1.27.0-rc.2'
cache: true
- name: Run go mod tidy
run: go mod tidy
@@ -36,7 +36,7 @@ jobs:
# statically linked binary.
runs-on: ubuntu-latest
container:
image: golang:1.27rc3-alpine
image: golang:1.27rc2-alpine
outputs:
version: ${{ steps.version.outputs.version }}
steps:
@@ -160,7 +160,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.27.0-rc.3'
go-version: '1.27.0-rc.2'
cache: true
- name: Install wasmtime
uses: bytecodealliance/actions/wasmtime/setup@v1
@@ -204,7 +204,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.27.0-rc.3'
go-version: '1.27.0-rc.2'
cache: true
- name: Install Node.js
uses: actions/setup-node@v6
@@ -323,7 +323,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.27.0-rc.3'
go-version: '1.27.0-rc.2'
cache: true
- name: Restore LLVM source cache
uses: actions/cache/restore@v5
+4 -4
View File
@@ -34,7 +34,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.27.0-rc.3'
go-version: '1.27.0-rc.2'
cache: true
- name: Restore cached LLVM source
uses: actions/cache/restore@v5
@@ -129,7 +129,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.27.0-rc.3'
go-version: '1.27.0-rc.2'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v8
@@ -150,7 +150,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.27.0-rc.3'
go-version: '1.27.0-rc.2'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v8
@@ -176,7 +176,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v6
with:
go-version: '1.27.0-rc.3'
go-version: '1.27.0-rc.2'
cache: true
- name: Download TinyGo build
uses: actions/download-artifact@v8
+2 -2
View File
@@ -1,5 +1,5 @@
# tinygo-llvm stage obtains the llvm source for TinyGo
FROM golang:1.27rc3 AS tinygo-llvm
FROM golang:1.27rc2 AS tinygo-llvm
RUN apt-get update && \
apt-get install -y apt-utils make cmake clang-17 ninja-build && \
@@ -33,7 +33,7 @@ RUN cd /tinygo/ && \
# tinygo-compiler copies the compiler build over to a base Go container (without
# all the build tools etc).
FROM golang:1.27rc3 AS tinygo-compiler
FROM golang:1.27rc2 AS tinygo-compiler
# Copy tinygo build.
COPY --from=tinygo-compiler-build /tinygo/build/release/tinygo /tinygo
+1 -8
View File
@@ -177,15 +177,8 @@ func Build(pkgName, outpath string, config *compileopts.Config) error {
// Pick a default output path based on the main directory.
outpath = filepath.Base(result.MainDir) + config.DefaultBinaryExtension()
}
} else if fi, statErr := os.Stat(outpath); statErr == nil && fi.IsDir() {
var name string
if strings.HasSuffix(pkgName, ".go") {
name = filepath.Base(pkgName[:len(pkgName)-3]) + config.DefaultBinaryExtension()
} else {
name = filepath.Base(result.MainDir) + config.DefaultBinaryExtension()
}
outpath = filepath.Join(outpath, name)
}
if err := os.Rename(result.Binary, outpath); err != nil {
// Moving failed. Do a file copy.
inf, err := os.Open(result.Binary)
+14
View File
@@ -68,6 +68,20 @@ func (dev *USBDevice) Configure(config UARTConfig) {
dev.initcomplete = true
}
// Attach connects the device to the USB bus, allowing the host to detect and
// enumerate it. It can be used together with Detach to delay enumeration
// until the USB configuration (device identifiers, classes, ...) is complete.
func (dev *USBDevice) Attach() {
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
}
// Detach disconnects the device from the USB bus. To the host this appears
// as if the device was unplugged. A subsequent Attach makes the host
// enumerate the device again.
func (dev *USBDevice) Detach() {
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH)
}
func handlePadCalibration() {
// Load Pad Calibration data from non-volatile memory
// This requires registers that are not included in the SVD file.
+14
View File
@@ -71,6 +71,20 @@ func (dev *USBDevice) Configure(config UARTConfig) {
dev.initcomplete = true
}
// Attach connects the device to the USB bus, allowing the host to detect and
// enumerate it. It can be used together with Detach to delay enumeration
// until the USB configuration (device identifiers, classes, ...) is complete.
func (dev *USBDevice) Attach() {
sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH)
}
// Detach disconnects the device from the USB bus. To the host this appears
// as if the device was unplugged. A subsequent Attach makes the host
// enumerate the device again.
func (dev *USBDevice) Detach() {
sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH)
}
func handlePadCalibration() {
// Load Pad Calibration data from non-volatile memory
// This requires registers that are not included in the SVD file.
+15
View File
@@ -89,6 +89,21 @@ func (dev *USBDevice) Configure(config UARTConfig) {
dev.initcomplete = true
}
// Attach connects the device to the USB bus by enabling the DP pull-up,
// allowing the host to detect and enumerate it. It can be used together with
// Detach to delay enumeration until the USB configuration (device
// identifiers, classes, ...) is complete.
func (dev *USBDevice) Attach() {
nrf.USBD.USBPULLUP.Set(1)
}
// Detach disconnects the device from the USB bus by disabling the DP pull-up.
// To the host this appears as if the device was unplugged. A subsequent
// Attach makes the host enumerate the device again.
func (dev *USBDevice) Detach() {
nrf.USBD.USBPULLUP.Set(0)
}
func handleUSBIRQ(interrupt.Interrupt) {
if nrf.USBD.EVENTS_SOF.Get() == 1 {
nrf.USBD.EVENTS_SOF.Set(0)
+15
View File
@@ -46,6 +46,21 @@ func (dev *USBDevice) Configure(config UARTConfig) {
rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN)
}
// Attach connects the device to the USB bus by enabling the DP pull-up,
// allowing the host to detect and enumerate it. It can be used together with
// Detach to delay enumeration until the USB configuration (device
// identifiers, classes, ...) is complete.
func (dev *USBDevice) Attach() {
rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN)
}
// Detach disconnects the device from the USB bus by disabling the DP pull-up.
// To the host this appears as if the device was unplugged. A subsequent
// Attach makes the host enumerate the device again.
func (dev *USBDevice) Detach() {
rp.USBCTRL_REGS.SIE_CTRL.ClearBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN)
}
func handleUSBIRQ(intr interrupt.Interrupt) {
status := rp.USBCTRL_REGS.INTS.Get()
+15
View File
@@ -49,6 +49,21 @@ func (dev *USBDevice) Configure(config UARTConfig) {
rp.USB.SetMAIN_CTRL_PHY_ISO(0x0)
}
// Attach connects the device to the USB bus by enabling the DP pull-up,
// allowing the host to detect and enumerate it. It can be used together with
// Detach to delay enumeration until the USB configuration (device
// identifiers, classes, ...) is complete.
func (dev *USBDevice) Attach() {
rp.USB.SIE_CTRL.SetBits(rp.USB_SIE_CTRL_PULLUP_EN)
}
// Detach disconnects the device from the USB bus by disabling the DP pull-up.
// To the host this appears as if the device was unplugged. A subsequent
// Attach makes the host enumerate the device again.
func (dev *USBDevice) Detach() {
rp.USB.SIE_CTRL.ClearBits(rp.USB_SIE_CTRL_PULLUP_EN)
}
func handleUSBIRQ(intr interrupt.Interrupt) {
status := rp.USB.INTS.Get()
+1 -1
View File
@@ -510,7 +510,7 @@ func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
lastBlock := firstBlock.findHead()
// Calculate the size of the original allocation body.
oldSize := uintptr(lastBlock-firstBlock)*bytesPerBlock + (bytesPerBlock - unsafe.Sizeof(objHeader{}))
oldSize := uintptr(lastBlock-firstBlock)*blocksPerStateByte + (bytesPerBlock - unsafe.Sizeof(objHeader{}))
if size <= oldSize {
// The requested size is less than the old size.
+1 -7
View File
@@ -47,14 +47,8 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
gcTotalAlloc += uint64(size)
gcMallocs++
heapptr += size
if heapptr < addr {
// The allocation size overflowed the heap pointer.
runtimePanic("out of memory")
}
for heapptr > heapEnd {
for heapptr >= heapEnd {
// Try to increase the heap and check again.
// Use > instead of >= because an allocation that exactly
// ends at heapEnd is still within heap boundaries.
if growHeap() {
continue
}
+7 -6
View File
@@ -43,12 +43,6 @@ func gcMarkReachable() {
gcPauseCore(i)
}
// Busy-wait until all the other cores are ready.
for gcScanState.Load() != numCPU {
spinLoopWait()
}
gcScanState.Store(0)
// Scan the stack(s) of the current core.
scanCurrentStack()
if !task.OnSystemStack() {
@@ -59,6 +53,13 @@ func gcMarkReachable() {
// Scan globals.
findGlobals(markRoots)
// Busy-wait until all the other cores are ready. They certainly should be,
// after the scanning we did above.
for gcScanState.Load() != numCPU {
spinLoopWait()
}
gcScanState.Store(0)
// Signal each core in turn that they can scan the stack.
for i := uint32(0); i < numCPU; i++ {
if i == core {
+2 -2
View File
@@ -807,13 +807,13 @@ func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
case reflectlite.Array:
var hash uint32
for i := 0; i < x.Len(); i++ {
hash = (hash * 31) ^ hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)), seed)
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)), seed)
}
return hash
case reflectlite.Struct:
var hash uint32
for i := 0; i < x.NumField(); i++ {
hash = (hash * 31) ^ hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)), seed)
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)), seed)
}
return hash
default:
+1 -1
View File
@@ -3,7 +3,7 @@
"inheritable-only": true,
"cpu": "esp32",
"features": "+atomctl,+bool,+clamps,+coprocessor,+debug,+density,+dfpaccel,+div32,+exception,+fp,+highpriinterrupts,+interrupt,+loop,+mac16,+memctl,+minmax,+miscsr,+mul32,+mul32high,+nsa,+prid,+regprotect,+rvector,+s32c1i,+sext,+threadptr,+timerint,+windowed",
"build-tags": ["esp32", "esp", "espradio"],
"build-tags": ["esp32", "esp"],
"scheduler": "tasks",
"serial": "uart",
"linker": "ld.lld",
+1 -2
View File
@@ -6,8 +6,7 @@
"features": "+32bit,+c,+m,+zmmul,-relax",
"build-tags": [
"esp32c3",
"esp",
"espradio"
"esp"
],
"serial": "usb",
"rtlib": "compiler-rt",
+1 -1
View File
@@ -3,7 +3,7 @@
"inheritable-only": true,
"cpu": "esp32s3",
"features": "+atomctl,+bool,+clamps,+coprocessor,+debug,+density,+div32,+esp32s3,+exception,+fp,+highpriinterrupts,+interrupt,+loop,+mac16,+memctl,+minmax,+miscsr,+mul32,+mul32high,+nsa,+prid,+regprotect,+rvector,+s32c1i,+sext,+threadptr,+timerint,+windowed",
"build-tags": ["esp32s3", "esp", "espradio"],
"build-tags": ["esp32s3", "esp"],
"scheduler": "tasks",
"serial": "usb",
"linker": "ld.lld",