Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package midi

import (
"runtime/volatile"
"github.com/goplus/emb/runtime/volatile"
)

const bufferSize = 128
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package midi

import (
"machine"
"machine/usb"
"machine/usb/descriptor"
"github.com/goplus/emb/machine"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Quality: Global Singleton Without Thread Safety

The global Midi variable is initialized in init() without synchronization. While this may work in single-threaded embedded contexts, it's not future-proof for concurrent access.

Recommendation:
Either:

  1. Use sync.Once for lazy initialization if concurrent access is possible
  2. Document clearly that this package assumes single-threaded execution
  3. Add build constraints if this is architecture-specific

"github.com/goplus/emb/machine/usb"
"github.com/goplus/emb/machine/usb/descriptor"
)

const (
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion machine/_usb/cdc/buffer.go → machine/usb/cdc/buffer.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cdc

import (
"runtime/volatile"
"github.com/goplus/emb/runtime/volatile"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Race Condition in Ring Buffer

The Put() operation is not atomic and lacks proper synchronization. Between checking Used() and updating the buffer, an interrupt could modify the buffer state, leading to data corruption.

Issues:

  1. Used() result can become stale between check at line 30 and buffer update at line 32
  2. No interrupt masking around the critical section
  3. Similar pattern exists in HID and MIDI ring buffers

Recommendation:
Use interrupt.Disable()/Restore() around critical sections (CDC code already uses this pattern elsewhere in usbcdc.go):

func (rb *rxRingBuffer) Put(val byte) bool {
    state := interrupt.Disable()
    defer interrupt.Restore(state)
    
    if rb.Used() != rxRingBufferSize {
        rb.head.Set(rb.head.Get() + 1)
        rb.buffer[rb.head.Get()%rxRingBufferSize].Set(val)
        return true
    }
    return false
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance: Redundant Volatile Register Reads

Each rb.head.Get() is an expensive volatile register read. Line 31-32 performs this twice.

Optimization for embedded systems:

newHead := rb.head.Get() + 1
rb.head.Set(newHead)
rb.buffer[newHead%rxRingBufferSize].Set(val)

Further optimization: Since rxRingBufferSize = 128 is a power of 2, use bitwise AND instead of modulo (much faster on ARM Cortex-M without hardware divider):

const rxRingBufferMask = rxRingBufferSize - 1
rb.buffer[newHead&rxRingBufferMask].Set(val)

)

const rxRingBufferSize = 128
Expand Down
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions machine/_usb/cdc/usbcdc.go → machine/usb/cdc/usbcdc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package cdc

import (
"errors"
"machine"
"machine/usb"
"runtime/interrupt"
"github.com/goplus/emb/machine"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMPORTANT: Silent Error Suppression

Errors from ReadByte() are silently ignored, potentially reading zero values instead of actual data.

Recommendation:
Either propagate the error or document why it can be safely ignored:

for i := 0; i < size; i++ {
    v, err := usbcdc.ReadByte()
    if err != nil {
        return i, err
    }
    data[i] = v
}

"github.com/goplus/emb/machine/usb"
"github.com/goplus/emb/runtime/interrupt"
)

var (
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package descriptor

import (
"internal/binary"
"github.com/goplus/emb/internal/binary"
)

const (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package descriptor

import (
"runtime/volatile"
"github.com/goplus/emb/runtime/volatile"
)

const (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package descriptor

import (
"internal/binary"
"github.com/goplus/emb/internal/binary"
)

const (
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package descriptor

import (
"internal/binary"
"github.com/goplus/emb/internal/binary"
)

/* Endpoint Descriptor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package descriptor

import (
"errors"
"internal/binary"
"internal/bytealg"
"github.com/goplus/emb/internal/binary"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance: Redundant Volatile Register Reads

Each call to rb.head.Get() is an expensive volatile register read. The value is fetched twice here (lines 31-32 in buffer implementations).

Optimization for embedded systems:

newHead := rb.head.Get() + 1
rb.head.Set(newHead)
rb.buffer[newHead%rxRingBufferSize].Set(val)

Additionally, since buffer sizes are powers of 2, use bitwise AND instead of modulo:

const rxRingBufferMask = rxRingBufferSize - 1
rb.buffer[newHead&rxRingBufferMask].Set(val)  // Much faster on ARM Cortex-M

"github.com/goplus/emb/internal/bytealg"
)

var configurationCDCHID = [configurationTypeLen]byte{
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion machine/_usb/hid/buffer.go → machine/usb/hid/buffer.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package hid

import (
"runtime/volatile"
"github.com/goplus/emb/runtime/volatile"
)

const bufferSize = 128
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions machine/_usb/hid/hid.go → machine/usb/hid/hid.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package hid

import (
"errors"
"machine"
"machine/usb"
"machine/usb/descriptor"
"github.com/goplus/emb/machine"
"github.com/goplus/emb/machine/usb"
"github.com/goplus/emb/machine/usb/descriptor"
)

// from usb-hid.go
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package joystick

import (
"machine"
"machine/usb"
"machine/usb/descriptor"
"machine/usb/hid"
"github.com/goplus/emb/machine"
"github.com/goplus/emb/machine/usb"
"github.com/goplus/emb/machine/usb/descriptor"
"github.com/goplus/emb/machine/usb/hid"
)

var Joystick *joystick
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package joystick

import (
"machine/usb/descriptor"
"github.com/goplus/emb/machine/usb/descriptor"

"encoding/binary"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package keyboard

import (
"errors"
"machine"
"machine/usb/hid"
"github.com/goplus/emb/machine"
"github.com/goplus/emb/machine/usb/hid"
)

// from usb-hid-keyboard.go
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package mouse

import (
"machine"
"machine/usb/hid"
"github.com/goplus/emb/machine"
"github.com/goplus/emb/machine/usb/hid"
)

var Mouse *mouse
Expand Down
4 changes: 2 additions & 2 deletions machine/_usb/msc/cbw.go → machine/usb/msc/cbw.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package msc

import (
"encoding/binary"
"machine/usb/msc/csw"
"machine/usb/msc/scsi"
"github.com/goplus/emb/machine/usb/msc/csw"
"github.com/goplus/emb/machine/usb/msc/scsi"
)

const (
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion machine/_usb/msc/disk.go → machine/usb/msc/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"encoding/binary"
"errors"
"fmt"
"machine"
"github.com/goplus/emb/machine"
"time"
)

Expand Down
10 changes: 5 additions & 5 deletions machine/_usb/msc/msc.go → machine/usb/msc/msc.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package msc

import (
"machine"
"machine/usb"
"machine/usb/descriptor"
"machine/usb/msc/csw"
"machine/usb/msc/scsi"
"github.com/goplus/emb/machine"
"github.com/goplus/emb/machine/usb"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance: Inefficient Busy-Wait Goroutine

This goroutine wakes 10,000 times per second to check a single boolean flag.

Issues:

  1. Continuous wakeup prevents deep sleep states (critical for battery-powered devices)
  2. Adds scheduler pressure in resource-constrained embedded systems
  3. Goroutine stack (~2KB minimum) permanently allocated

Recommendation:
Use channel-based signaling for event notification:

type msc struct {
    // ... existing fields ...
    taskChan chan struct{}
}

func (m *msc) processTasks() {
    for range m.taskChan {
        cmd := m.cbw.SCSICmd()
        // ... process task ...
        m.queuedBytes = 0
        m.taskQueued = false
        machine.AckUsbOutTransfer(usb.MSC_ENDPOINT_OUT)
    }
}

// In rxHandler, send signal: select { case m.taskChan <- struct{}{}: default: }

"github.com/goplus/emb/machine/usb/descriptor"
"github.com/goplus/emb/machine/usb/msc/csw"
"github.com/goplus/emb/machine/usb/msc/scsi"
"time"
)

Expand Down
6 changes: 3 additions & 3 deletions machine/_usb/msc/scsi.go → machine/usb/msc/scsi.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package msc

import (
"encoding/binary"
"machine/usb"
"machine/usb/msc/csw"
"machine/usb/msc/scsi"
"github.com/goplus/emb/machine/usb"
"github.com/goplus/emb/machine/usb/msc/csw"
"github.com/goplus/emb/machine/usb/msc/scsi"
)

func (m *msc) scsiCmdBegin() {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package msc

import (
"encoding/binary"
"machine/usb/msc/csw"
"machine/usb/msc/scsi"
"github.com/goplus/emb/machine/usb/msc/csw"
"github.com/goplus/emb/machine/usb/msc/scsi"
)

type vpdPage struct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package msc

import (
"errors"
"machine/usb/msc/csw"
"machine/usb/msc/scsi"
"github.com/goplus/emb/machine/usb/msc/csw"
"github.com/goplus/emb/machine/usb/msc/scsi"
)

var invalidWriteError = errors.New("invalid write offset or length")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package msc

import (
"encoding/binary"
"machine/usb/msc/csw"
"machine/usb/msc/scsi"
"github.com/goplus/emb/machine/usb/msc/csw"
"github.com/goplus/emb/machine/usb/msc/scsi"
)

type Error int
Expand Down
6 changes: 3 additions & 3 deletions machine/_usb/msc/setup.go → machine/usb/msc/setup.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package msc

import (
"machine"
"machine/usb"
"machine/usb/msc/csw"
"github.com/goplus/emb/machine"
"github.com/goplus/emb/machine/usb"
"github.com/goplus/emb/machine/usb/msc/csw"
)

func setupPacketHandler(setup usb.Setup) bool {
Expand Down
File renamed without changes.
Loading