functional USB CDC-ACM for SAMx51

This commit is contained in:
ardnew
2022-02-19 23:27:35 -06:00
committed by sago35
parent 5f6489d3cf
commit 3b892cbe41
6 changed files with 216 additions and 61 deletions
+27 -2
View File
@@ -532,8 +532,8 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage {
// Control/status interface:
case descCDCACMInterfaceCtrl:
// DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02)
d.uartSetLineState(sup.wValue)
// CDC Control Line State packet receipt handling occurs in method
// controlComplete().
d.controlReceive(uintptr(0), 0, false)
return dcdStageStatusOut
@@ -695,6 +695,31 @@ func (d *dcd) controlComplete() {
// Unhandled device class
}
// CDC | SET CONTROL LINE STATE (0x22):
case descCDCRequestSetControlLineState:
// Respond based on our device class configuration
switch d.cc.id {
// CDC-ACM (single)
case classDeviceCDCACM:
// Determine interface destination of the request
switch d.setup.wIndex {
// Control/status interface:
case descCDCACMInterfaceCtrl:
// DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02)
d.uartSetLineState(d.setup.wValue)
default:
// Unhandled device interface
}
default:
// Unhandled device class
}
// HID | SET REPORT (0x09)
case descHIDRequestSetReport:
+30
View File
@@ -1,5 +1,7 @@
package usb
import "runtime/volatile"
const descUSBSpecVersion = uint16(0x0200) // USB 2.0
const descLanguageEnglish = uint16(0x0409) // (US) English
@@ -481,6 +483,26 @@ func (s *descCDCACMLineState) parse(v uint16) bool {
return true
}
type descCDCACMState uint8
const (
descCDCACMStateConfigured descCDCACMState = iota // Received SET_CONFIGURATION class request
descCDCACMStateLineState // Received SET_LINE_STATE after Configured state
descCDCACMStateLineCoding // Received SET_LINE_CODING after LineState state
)
func (s *descCDCACMState) set(state descCDCACMState) {
if state > *s {
// state must be incremented in-order. Otherwise, reset to initial state.
if state == *s+1 {
*s = state
} else {
var init descCDCACMState // Reset to zero-value of type.
*s = init
}
}
}
// Common configuration constants for the USB CDC-ACM (single) device class.
const (
descCDCACMLanguageCount = 1 // String descriptor languages available
@@ -510,6 +532,14 @@ type descCDCACMClass struct {
device *[descLengthDevice]uint8 // device descriptor
qualif *[descLengthQualification]uint8 // device qualification descriptor
config *[descCDCACMConfigSize]uint8 // configuration descriptor
state volatile.Register8
}
func (c *descCDCACMClass) setState(state descCDCACMState) {
s := descCDCACMState(c.state.Get())
s.set(state)
c.state.Set(uint8(s))
}
// descCDCACM holds statically-allocated instances for each of the CDC-ACM
+18 -2
View File
@@ -66,8 +66,8 @@ const (
// CDC-ACM Data Buffers
descCDCACMRxSize = 1 * descCDCACMDataRxPacketSize
descCDCACMTxSize = 1 * descCDCACMDataTxPacketSize
descCDCACMRxSize = descCDCACMDataRxPacketSize
descCDCACMTxSize = descCDCACMDataTxPacketSize
descCDCACMTxTimeoutMs = 120 // millisec
descCDCACMTxSyncUs = 75 // microsec
@@ -218,6 +218,16 @@ var descCDCACM0Rx [descCDCACMRxSize]uint8
//go:align 32
var descCDCACM0Tx [descCDCACMTxSize]uint8
// descCDCACM0Rq is the receive (Rx) transfer buffer for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDCACM0Rq [descCDCACMRxSize]uint8
// descCDCACM0Tq is the transmit (Tx) transfer buffer for the default CDC-ACM
// (single) device class configuration (index 1).
//go:align 32
var descCDCACM0Tq [descCDCACMTxSize]uint8
// descCDCACM0LC is the emulated UART's line coding configuration for the
// default CDC-ACM (single) device class configuration (index 1).
//go:align 32
@@ -252,6 +262,9 @@ type descCDCACMClassData struct {
rx *[descCDCACMRxSize]uint8 // bulk data endpoint Rx (OUT) transfer buffer
tx *[descCDCACMTxSize]uint8 // bulk data endpoint Tx (IN) transfer buffer
rxq *[descCDCACMRxSize]uint8
txq *[descCDCACMTxSize]uint8
lc *descCDCACMLineCoding // UART line coding
ls *descCDCACMLineState // UART line state
@@ -284,6 +297,9 @@ var descCDCACMData = [dcdCount]descCDCACMClassData{
rx: &descCDCACM0Rx,
tx: &descCDCACM0Tx,
rxq: &descCDCACM0Rq,
txq: &descCDCACM0Tq,
lc: &descCDCACM0LC,
ls: &descCDCACM0LS,
+116 -41
View File
@@ -1334,17 +1334,19 @@ func (d *dhw) uartConfigure() {
acm := &descCDCACM[d.cc.config-1]
acm.setState(descCDCACMStateConfigured)
// SAMx51 only supports USB full-speed (FS) operation
acm.sxSize = descCDCACMStatusFSPacketSize
acm.rxSize = descCDCACMDataRxFSPacketSize
acm.txSize = descCDCACMDataTxFSPacketSize
rq := acm.rx[:]
tq := acm.tx[:]
rq := acm.rxq[:]
tq := acm.txq[:]
// Rx gives priority to incoming data, Tx gives priority to outgoing data
acm.rq.Init(&rq, descCDCACMRxSize, QueueFullDiscardFirst)
acm.tq.Init(&tq, descCDCACMTxSize, QueueFullDiscardLast)
acm.rq.Init(&rq, int(acm.rxSize), QueueFullDiscardFirst)
acm.tq.Init(&tq, int(acm.txSize), QueueFullDiscardLast)
d.endpointEnable(txEndpoint(descCDCACMEndpointStatus),
false, descCDCACMConfigAttrStatus)
@@ -1358,13 +1360,14 @@ func (d *dhw) uartConfigure() {
d.endpointConfigure(rxEndpoint(descCDCACMEndpointDataRx),
d.uartReceiveComplete)
d.endpointConfigure(txEndpoint(descCDCACMEndpointDataTx),
nil)
d.uartTransmitComplete)
d.uartReceive(descCDCACMEndpointDataRx)
d.uartReceiveStart(rxEndpoint(descCDCACMEndpointDataRx))
}
func (d *dhw) uartSetLineState(state uint16) {
acm := &descCDCACM[d.cc.config-1]
acm.setState(descCDCACMStateLineState)
if acm.ls.parse(state) {
// TBD: respond to changes in line state?
}
@@ -1372,6 +1375,7 @@ func (d *dhw) uartSetLineState(state uint16) {
func (d *dhw) uartSetLineCoding(coding []uint8) {
acm := &descCDCACM[d.cc.config-1]
acm.setState(descCDCACMStateLineCoding)
if acm.lc.parse(coding) {
switch acm.lc.baud {
case 1200:
@@ -1383,10 +1387,24 @@ func (d *dhw) uartSetLineCoding(coding []uint8) {
}
func (d *dhw) uartReady() bool {
return d.dcd.state() == dcdStateConfigured
acm := &descCDCACM[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).
//
// 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.
//
// The "set" method on type descCDCACMState defines this incremental state
// machine, with the UART's current state stored in the volatile.Register8
// field "state" of descCDCACMClass.
return d.state() == dcdStateConfigured && //acm.ls.dataTerminalReady &&
acm.state.Get() == uint8(descCDCACMStateLineCoding)
}
func (d *dhw) uartReceive(endpoint uint8) {
func (d *dhw) uartReceiveStart(endpoint uint8) {
acm := &descCDCACM[d.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
@@ -1396,8 +1414,7 @@ func (d *dhw) uartReceive(endpoint uint8) {
if xfer, ok := d.ep[num][descDirRx].pendingTransfer(); ok {
// Update the active transfer descriptor on the corresponding endpoint.
d.ep[num][descDirRx].setActiveTransfer(xfer)
next := xfer.packetStart(xfer.data, xfer.size)
d.endpointTransfer(endpoint, xfer.data, next)
d.endpointTransfer(endpoint, xfer.data, xfer.size)
}
}
}
@@ -1405,65 +1422,123 @@ func (d *dhw) uartReceive(endpoint uint8) {
func (d *dhw) uartReceiveComplete(endpoint uint8, size uint32) {
acm := &descCDCACM[d.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
_ = acm // TODO(ardnew): elaborate stub
if xfer, ok := d.ep[num][descDirRx].activeTransfer(); ok {
for ptr := xfer.data + uintptr(xfer.sent); ptr < xfer.data+uintptr(size); ptr++ {
for ptr := xfer.data; ptr < xfer.data+uintptr(size); ptr++ {
acm.rq.Enq(*(*uint8)(unsafe.Pointer(ptr)))
}
if data, size := xfer.packetComplete(size); size > 0 {
d.endpointTransfer(endpoint, data, size)
return
}
}
d.ep[num][descDirRx].setActiveTransfer(nil)
d.uartReceive(endpoint)
d.uartReceiveStart(endpoint)
}
func (d *dhw) uartTransmitStart(endpoint uint8) {
acm := &descCDCACM[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) uartTransmitComplete(endpoint uint8, size uint32) {
acm := &descCDCACM[d.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
if size > 0 && size%acm.txSize == 0 {
// Send ZLP if transfer length is a multiple of max packet size.
d.endpointTransfer(endpoint, 0, 0)
}
d.ep[num][descDirTx].setActiveTransfer(nil)
d.uartTransmitStart(endpoint)
}
// uartFlush discards all buffered input (Rx) data.
func (d *dhw) uartFlush() {
acm := &descCDCACM[d.cc.config-1]
_ = acm // TODO(ardnew): elaborate stub
acm.rq.Reset(int(acm.rxSize))
}
func (d *dhw) uartAvailable() int {
return 0
acm := &descCDCACM[d.cc.config-1]
return acm.rq.Len()
}
func (d *dhw) uartPeek() (uint8, bool) {
acm := &descCDCACM[d.cc.config-1]
_ = acm // TODO(ardnew): elaborate stub
return 0, false
return acm.rq.Front()
}
func (d *dhw) uartReadByte() (uint8, bool) {
b := []uint8{0}
ok := d.uartRead(b) > 0
return b[0], ok
}
func (d *dhw) uartRead(data []uint8) int {
acm := &descCDCACM[d.cc.config-1]
read := uint16(0)
size := uint16(len(data))
_, _ = acm, size // TODO(ardnew): elaborate stub
return int(read)
return acm.rq.Deq()
}
func (d *dhw) uartWriteByte(c uint8) bool {
return d.uartWrite([]uint8{c}) == 1
}
func (d *dhw) uartWrite(data []uint8) int {
func (d *dhw) uartRead(data []uint8) (int, error) {
acm := &descCDCACM[d.cc.config-1]
sent := 0
size := len(data)
_, _ = acm, size // TODO(ardnew): elaborate stub
return sent
return acm.rq.Read(data)
}
func (d *dhw) uartSync() {
func (d *dhw) uartWriteByte(c uint8) error {
_, err := d.uartWrite([]uint8{c})
return err
}
func (d *dhw) uartWrite(data []uint8) (int, error) {
acm := &descCDCACM[d.cc.config-1]
num := uint16(descCDCACMEndpointDataTx) & 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.uartTransmitStart(txEndpoint(descCDCACMEndpointDataTx))
}
}
return sent, werr
}
// =============================================================================
+13 -9
View File
@@ -21,11 +21,11 @@ type Queue struct {
}
var (
ErrReadBuffer = errors.New("cannot copy into read buffer")
ErrWriteBuffer = errors.New("cannot copy from write buffer")
ErrQueueEmpty = errors.New("data queue empty") // Read Error
ErrQueueFull = errors.New("data queue full") // Write error
ErrQueueNoMode = errors.New("unknown FIFO copy mode")
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
@@ -117,7 +117,7 @@ func (q *Queue) Read(data []uint8) (int, error) {
less := uint32(len(data))
if less == 0 {
return 0, ErrReadBuffer
return 0, ErrQueueReadZero
} // nothing to copy into
head := q.head.Get()
@@ -152,7 +152,7 @@ func (q *Queue) Write(data []uint8) (int, error) {
// Nothing to copy from is an error regardless of mode.
if more == 0 {
return 0, ErrWriteBuffer
return 0, ErrQueueWriteZero
}
switch q.mode {
@@ -167,7 +167,7 @@ func (q *Queue) Write(data []uint8) (int, error) {
return 0, ErrQueueFull
}
// xOnly put to unused space.
// Only put to unused space.
if used+more > q.size.Get() {
more = q.size.Get() - used
}
@@ -210,11 +210,15 @@ func (q *Queue) Write(data []uint8) (int, error) {
// 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]
(*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
+12 -7
View File
@@ -6,9 +6,7 @@ import (
var (
ErrUARTInvalidPort = errors.New("invalid USB port")
ErrUARTInvalidCore = errors.New("invalid USB core")
ErrUARTEmptyBuffer = errors.New("USB receive buffer empty")
ErrUARTWriteFailed = errors.New("USB write failure")
)
// UART represents a virtual serial (UART) device emulation using the USB
@@ -43,12 +41,16 @@ func (uart *UART) Ready() bool {
// Buffered returns the number of bytes currently stored in the RX buffer.
func (uart *UART) Buffered() int {
for !uart.Ready() {
}
return uart.core.dc.uartAvailable()
}
// ReadByte reads a single byte from the RX buffer.
// If there is no data in the buffer, returns an error.
func (uart *UART) ReadByte() (byte, error) {
for !uart.Ready() {
}
n, ok := uart.core.dc.uartReadByte()
if !ok {
return 0, ErrUARTEmptyBuffer
@@ -58,18 +60,21 @@ func (uart *UART) ReadByte() (byte, error) {
// Read from the RX buffer.
func (uart *UART) Read(data []byte) (n int, err error) {
return uart.core.dc.uartRead(data), nil
for !uart.Ready() {
}
return uart.core.dc.uartRead(data)
}
// WriteByte writes a single byte of data to the UART interface.
func (uart *UART) WriteByte(c byte) error {
if !uart.core.dc.uartWriteByte(c) {
return ErrUARTWriteFailed
for !uart.Ready() {
}
return nil
return uart.core.dc.uartWriteByte(c)
}
// Write data to the UART.
func (uart *UART) Write(data []byte) (n int, err error) {
return uart.core.dc.uartWrite(data), nil
for !uart.Ready() {
}
return uart.core.dc.uartWrite(data)
}