mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +00:00
add basic CDC-ACM UART Rx capability
This commit is contained in:
+4
-11
@@ -505,11 +505,10 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage {
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
// line coding must contain exactly 7 bytes
|
||||
if uint16(descCDCACMCodingSize) == sup.wLength {
|
||||
d.setup = sup
|
||||
if uint16(descCDCACMLineCodingSize) == sup.wLength {
|
||||
d.controlReceive(
|
||||
uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].cx[0])),
|
||||
uint32(descCDCACMCodingSize), true)
|
||||
uint32(descCDCACMLineCodingSize), true)
|
||||
// CDC Line Coding packet receipt handling occurs in method
|
||||
// controlComplete().
|
||||
return dcdStageDataOut
|
||||
@@ -534,7 +533,7 @@ 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(0 != sup.wValue&0x01, 0 != sup.wValue&0x02)
|
||||
d.uartSetLineState(sup.wValue)
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageStatusOut
|
||||
|
||||
@@ -570,7 +569,6 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage {
|
||||
// HID
|
||||
case classDeviceHID:
|
||||
if sup.wLength <= descHIDSxSize {
|
||||
d.setup = sup
|
||||
descHID[d.cc.config-1].cx[0] = 0xE9
|
||||
d.controlReceive(
|
||||
uintptr(unsafe.Pointer(&descHID[d.cc.config-1].cx[0])),
|
||||
@@ -687,12 +685,7 @@ func (d *dcd) controlComplete() {
|
||||
case descCDCACMInterfaceCtrl:
|
||||
// Notify PHY to handle triggers like special baud rates, which
|
||||
// signal to reboot into bootloader or begin receiving OTA updates
|
||||
d.uartSetLineCoding(descCDCACMLineCoding{
|
||||
baud: packU32(acm.cx[:]),
|
||||
stopBits: acm.cx[4],
|
||||
parity: acm.cx[5],
|
||||
numBits: acm.cx[6],
|
||||
})
|
||||
d.uartSetLineCoding(acm.cx[:])
|
||||
|
||||
default:
|
||||
// Unhandled device interface
|
||||
|
||||
+45
-7
@@ -1,7 +1,5 @@
|
||||
package usb
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const descUSBSpecVersion = uint16(0x0200) // USB 2.0
|
||||
|
||||
const descLanguageEnglish = uint16(0x0409) // (US) English
|
||||
@@ -140,6 +138,9 @@ const (
|
||||
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
|
||||
@@ -429,15 +430,55 @@ const (
|
||||
// descEndpointInvalid represents an invalid endpoint address.
|
||||
const descEndpointInvalid = ^uint8(descEndptAddrNumberMsk | descEndptAddrDirectionMsk)
|
||||
|
||||
// descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer.
|
||||
const descCDCACMCodingSize = unsafe.Sizeof(descCDCACMLineCoding{})
|
||||
// descCDCACMLineCodingSize 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, descCDCACMLineCodingSize defines the number of bytes
|
||||
// that are transferred following a control SETUP request.
|
||||
const descCDCACMLineCodingSize = 7
|
||||
|
||||
// descCDCACMLineCoding represents an emulated UART's line configuration.
|
||||
//
|
||||
// Use descCDCACMLineCodingSize instead of unsafe.Sizeof(descCDCACMLineCoding)
|
||||
// in any transfer requests, because the actual struct is padded for alignment.
|
||||
type descCDCACMLineCoding struct {
|
||||
baud uint32
|
||||
stopBits uint8
|
||||
parity uint8
|
||||
numBits uint8
|
||||
_ uint8
|
||||
}
|
||||
|
||||
// parse initializes the receiver descCDCACMLineCoding 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 *descCDCACMLineCoding) parse(v []uint8) bool {
|
||||
if len(v) >= descCDCACMLineCodingSize {
|
||||
s.baud = packU32(v[:])
|
||||
s.stopBits = v[4]
|
||||
s.parity = v[5]
|
||||
s.numBits = v[6]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// descCDCACMLineState represents an emulated UART's line state.
|
||||
type descCDCACMLineState 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 descCDCACMLineState 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 *descCDCACMLineState) 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.
|
||||
@@ -465,9 +506,6 @@ const (
|
||||
type descCDCACMClass struct {
|
||||
*descCDCACMClassData // Target-defined, class-specific data
|
||||
|
||||
line descCDCACMLineCoding
|
||||
term struct{ dtr, rts bool }
|
||||
|
||||
locale *[descCDCACMLanguageCount]descStringLanguage // string descriptors
|
||||
device *[descLengthDevice]uint8 // device descriptor
|
||||
qualif *[descLengthQualification]uint8 // device qualification descriptor
|
||||
|
||||
@@ -38,9 +38,6 @@ const (
|
||||
|
||||
descMaxEndpoints = 8 // SAMx51 maximum number of endpoints
|
||||
|
||||
descBankOut = 0 // descriptor bank 0 holds OUT endpoints
|
||||
descBankIn = 1 // descriptor bank 1 holds IN endpoints
|
||||
|
||||
descControlPacketSize = 64
|
||||
)
|
||||
|
||||
@@ -69,8 +66,8 @@ const (
|
||||
|
||||
// CDC-ACM Data Buffers
|
||||
|
||||
descCDCACMRxSize = 4 * descCDCACMDataRxPacketSize
|
||||
descCDCACMTxSize = 4 * descCDCACMDataTxPacketSize
|
||||
descCDCACMRxSize = 1 * descCDCACMDataRxPacketSize
|
||||
descCDCACMTxSize = 1 * descCDCACMDataTxPacketSize
|
||||
|
||||
descCDCACMTxTimeoutMs = 120 // millisec
|
||||
descCDCACMTxSyncUs = 75 // microsec
|
||||
@@ -221,6 +218,16 @@ var descCDCACM0Rx [descCDCACMRxSize]uint8
|
||||
//go:align 32
|
||||
var descCDCACM0Tx [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
|
||||
var descCDCACM0LC descCDCACMLineCoding
|
||||
|
||||
// descCDCACM0LS is the emulated UART's line state for the default CDC-ACM
|
||||
// (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0LS descCDCACMLineState
|
||||
|
||||
// descCDCACMClassData holds the buffers and control states for all CDC-ACM
|
||||
// (single) device class configurations, ordered by index (offset by -1), for
|
||||
// SAMx51 targets only.
|
||||
@@ -245,9 +252,15 @@ type descCDCACMClassData struct {
|
||||
rx *[descCDCACMRxSize]uint8 // bulk data endpoint Rx (OUT) transfer buffer
|
||||
tx *[descCDCACMTxSize]uint8 // bulk data endpoint Tx (IN) transfer buffer
|
||||
|
||||
sxSize uint16
|
||||
rxSize uint16
|
||||
txSize uint16
|
||||
lc *descCDCACMLineCoding // UART line coding
|
||||
ls *descCDCACMLineState // UART line state
|
||||
|
||||
rq *Queue
|
||||
tq *Queue
|
||||
|
||||
sxSize uint32
|
||||
rxSize uint32
|
||||
txSize uint32
|
||||
}
|
||||
|
||||
// descCDCACMData holds statically-allocated instances for each of the target-
|
||||
@@ -271,6 +284,12 @@ var descCDCACMData = [dcdCount]descCDCACMClassData{
|
||||
rx: &descCDCACM0Rx,
|
||||
tx: &descCDCACM0Tx,
|
||||
|
||||
lc: &descCDCACM0LC,
|
||||
ls: &descCDCACM0LS,
|
||||
|
||||
rq: &Queue{},
|
||||
tq: &Queue{},
|
||||
|
||||
sxSize: descCDCACMStatusPacketSize,
|
||||
rxSize: descCDCACMDataRxPacketSize,
|
||||
txSize: descCDCACMDataTxPacketSize,
|
||||
|
||||
+108
-102
@@ -9,6 +9,7 @@ package usb
|
||||
import (
|
||||
"device/arm"
|
||||
"device/sam"
|
||||
"math/bits"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
@@ -34,31 +35,11 @@ type dhw struct {
|
||||
|
||||
ep [descMaxEndpoints]dhwEPAddrStatus
|
||||
|
||||
log [2048][64]byte
|
||||
logCount uint
|
||||
|
||||
setup dcdSetup
|
||||
stage dcdStage
|
||||
address uint16
|
||||
}
|
||||
|
||||
var a uint
|
||||
|
||||
func (d *dhw) logEvent(s string) {
|
||||
d.enableInterrupts(false)
|
||||
copy(d.log[d.logCount][:], s)
|
||||
d.logCount++
|
||||
if d.logCount > 20 {
|
||||
d.logReset()
|
||||
}
|
||||
d.enableInterrupts(true)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func (d *dhw) logReset() {
|
||||
a = d.logCount
|
||||
}
|
||||
|
||||
func deleteCache(addr, size uintptr) {}
|
||||
func flushCache(addr, size uintptr) {}
|
||||
func runBootloader() {}
|
||||
@@ -86,20 +67,12 @@ func allocDHW(port, instance int, speed Speed, dc *dcd) *dhw {
|
||||
}
|
||||
|
||||
// SAMx51 has only one USB PHY, which is full-speed
|
||||
if 0 == speed {
|
||||
if speed == 0 {
|
||||
speed = FullSpeed
|
||||
}
|
||||
dhwInstance[instance].speed = speed
|
||||
dhwInstance[instance].ready = false
|
||||
|
||||
// initialize the transfer descriptors
|
||||
for i := range dhwInstance[instance].ep {
|
||||
dhwInstance[instance].ep[i][descBankOut].init(
|
||||
&dhwInstance[instance], rxEndpoint(uint8(i)))
|
||||
dhwInstance[instance].ep[i][descBankIn].init(
|
||||
&dhwInstance[instance], txEndpoint(uint8(i)))
|
||||
}
|
||||
|
||||
return &dhwInstance[instance]
|
||||
}
|
||||
|
||||
@@ -327,12 +300,12 @@ func (d *dhw) interrupt() {
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP |
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0)
|
||||
|
||||
d.logEvent("setup received")
|
||||
|
||||
// Parse the SETUP packet immediately, clearing room in the (one and only)
|
||||
// control buffer for the next SETUP packet received.
|
||||
sup := setupFrom(d.controlSetupBuffer())
|
||||
dir := sup.direction()
|
||||
|
||||
// We've copied the SETUP packet elsewhere and are ready to receive another.
|
||||
d.prepareSetup()
|
||||
|
||||
// Although there is only one control buffer, EP0 has two transfer queues:
|
||||
@@ -350,14 +323,12 @@ func (d *dhw) interrupt() {
|
||||
}
|
||||
}
|
||||
|
||||
// maybe transfer complete
|
||||
epints := d.bus.EPINTSMRY.Get() & ((1 << descMaxEndpoints) - 1)
|
||||
|
||||
epints := d.bus.EPINTSMRY.Get()
|
||||
for epints != 0 {
|
||||
|
||||
for ep := uint8(0); ep < descMaxEndpoints; ep++ {
|
||||
if (epints & (1 << ep)) == 0 {
|
||||
continue
|
||||
}
|
||||
ep := uint8(bits.TrailingZeros16(epints))
|
||||
epints &^= 1 << ep
|
||||
|
||||
intFlag := d.bus.DEVICE_ENDPOINT[ep].EPINTFLAG.Get()
|
||||
|
||||
@@ -378,22 +349,20 @@ func (d *dhw) interrupt() {
|
||||
|
||||
d.bus.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1)
|
||||
d.controlStall(false, descDirIn)
|
||||
|
||||
if ep == d.controlEndpoint() {
|
||||
d.logEvent("EP0 Tx packet complete")
|
||||
d.controlStall(false, descDirTx)
|
||||
// check if there is more data to transfer or if we need to notify the
|
||||
// upper-layer device driver of a control transfer completion event.
|
||||
if count == 0 || count < size {
|
||||
d.logEvent("EP0 Tx transfer complete")
|
||||
d.controlTransferComplete(txEndpoint(ep), count, total)
|
||||
} else {
|
||||
d.controlTransferContinue(txEndpoint(ep), count, total)
|
||||
}
|
||||
} else if nil != d.ep[ep][descBankIn].callback {
|
||||
} else if nil != d.ep[ep][descDirTx].callback {
|
||||
// call our device class-specific callback, if defined, on endpoint
|
||||
// data transfer complete events.
|
||||
d.ep[ep][descBankIn].callback(txEndpoint(ep), count)
|
||||
d.ep[ep][descDirTx].callback(txEndpoint(ep), count)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,22 +381,20 @@ func (d *dhw) interrupt() {
|
||||
|
||||
d.bus.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0)
|
||||
d.controlStall(false, descDirOut)
|
||||
|
||||
if ep == d.controlEndpoint() {
|
||||
d.logEvent("EP0 Rx packet complete")
|
||||
d.controlStall(false, descDirRx)
|
||||
// check if there is more data to transfer or if we need to notify the
|
||||
// upper-layer device driver of a control transfer completion event.
|
||||
if count == 0 || count < size {
|
||||
d.logEvent("EP0 Rx transfer complete")
|
||||
d.controlTransferComplete(rxEndpoint(ep), count, total)
|
||||
} else {
|
||||
d.controlTransferContinue(rxEndpoint(ep), count, total)
|
||||
}
|
||||
} else if nil != d.ep[ep][descBankOut].callback {
|
||||
} else if nil != d.ep[ep][descDirRx].callback {
|
||||
// call our device class-specific callback, if defined, on endpoint
|
||||
// data transfer complete events.
|
||||
d.ep[ep][descBankOut].callback(rxEndpoint(ep), count)
|
||||
d.ep[ep][descDirRx].callback(rxEndpoint(ep), count)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -532,10 +499,11 @@ func (d *dhw) controlStatusStart(endpoint uint8) {
|
||||
|
||||
num, dir := unpackEndpoint(endpoint)
|
||||
|
||||
// Swap direction of the given endpoint Rx->Tx and Tx->Rx
|
||||
switch dir {
|
||||
case descDirOut:
|
||||
case descDirRx:
|
||||
endpoint = txEndpoint(num)
|
||||
case descDirIn:
|
||||
case descDirTx:
|
||||
endpoint = rxEndpoint(num)
|
||||
}
|
||||
d.endpointTransfer(endpoint, 0, 0)
|
||||
@@ -543,8 +511,6 @@ func (d *dhw) controlStatusStart(endpoint uint8) {
|
||||
|
||||
func (d *dhw) controlStatusComplete(endpoint uint8) {
|
||||
|
||||
d.logEvent("setup processing complete")
|
||||
|
||||
if (d.setup.bmRequestType&descRequestTypeTypeMsk == descRequestTypeTypeStandard) &&
|
||||
(d.setup.bmRequestType&(descRequestTypeRecipientMsk|descRequestTypeDirMsk) ==
|
||||
descRequestTypeRecipientDevice|descRequestTypeDirOut) &&
|
||||
@@ -564,7 +530,6 @@ func (d *dhw) controlTransferStart(endpoint uint8) {
|
||||
|
||||
// Dequeue the next transfer descriptor available.
|
||||
if xfer, ok := d.ep[num][dir].pendingTransfer(); ok {
|
||||
d.logEvent("setup processing begin")
|
||||
// Update the active transfer descriptor on the corresponding endpoint.
|
||||
d.ep[num][dir].setActiveTransfer(xfer)
|
||||
// Invoke the DCD event handler for SETUP processing, which will enqueue
|
||||
@@ -593,8 +558,6 @@ func (d *dhw) controlTransferComplete(endpoint uint8, count, total uint32) {
|
||||
setupDir := d.setup.direction()
|
||||
setupAddress := packEndpoint(num, setupDir)
|
||||
|
||||
d.logEvent("setup packet complete")
|
||||
|
||||
// If endpoint direction is opposite the direction in the original SETUP
|
||||
// packet, then this is the end of the STATUS stage, i.e., end of transfer.
|
||||
if dir != setupDir {
|
||||
@@ -607,7 +570,6 @@ func (d *dhw) controlTransferComplete(endpoint uint8, count, total uint32) {
|
||||
// Start processing any pending control transfers.
|
||||
d.controlTransferStart(setupAddress)
|
||||
} else {
|
||||
d.logEvent("control status phase")
|
||||
// Initiate ZLP transfer in opposite direction.
|
||||
d.controlStatusStart(endpoint)
|
||||
}
|
||||
@@ -619,7 +581,7 @@ func (d *dhw) controlTransferComplete(endpoint uint8, count, total uint32) {
|
||||
func (d *dhw) controlReceive(data uintptr, size uint32, notify bool) {
|
||||
ep := d.controlEndpoint()
|
||||
if size > 0 && data > 0 {
|
||||
if xfer, ok := d.ep[ep][descBankOut].activeTransfer(); ok {
|
||||
if xfer, ok := d.ep[ep][descDirRx].activeTransfer(); ok {
|
||||
next := xfer.packetStart(data, size)
|
||||
d.endpointTransfer(rxEndpoint(ep), data, next)
|
||||
}
|
||||
@@ -634,7 +596,7 @@ func (d *dhw) controlReceive(data uintptr, size uint32, notify bool) {
|
||||
func (d *dhw) controlTransmit(data uintptr, size uint32, notify bool) {
|
||||
ep := d.controlEndpoint()
|
||||
if size > 0 && data > 0 {
|
||||
if xfer, ok := d.ep[ep][descBankIn].activeTransfer(); ok {
|
||||
if xfer, ok := d.ep[ep][descDirTx].activeTransfer(); ok {
|
||||
next := xfer.packetStart(data, size)
|
||||
d.endpointTransfer(txEndpoint(ep), data, next)
|
||||
}
|
||||
@@ -658,9 +620,9 @@ type dhwTransfer struct {
|
||||
|
||||
// dhwTransferDepth defines the size of the dhwEPStatus.xferQueue buffered channel,
|
||||
// which affects the number of transfers each endpoint can enqueue for processing.
|
||||
// const dhwTransferDepth = 8
|
||||
const dhwTransferDepth = 8
|
||||
|
||||
type dhwTransferLUT [QueueSize]dhwTransfer
|
||||
type dhwTransferLUT [dhwTransferDepth]dhwTransfer
|
||||
|
||||
func (t *dhwTransfer) init(endpoint uint8, maxPacketSize uint32) {
|
||||
t.endpoint = endpoint
|
||||
@@ -741,6 +703,7 @@ type dhwEPStatus struct {
|
||||
callback func(endpoint uint8, size uint32)
|
||||
flags volatile.Register8
|
||||
xferActive volatile.Register32
|
||||
xferFIFO [dhwTransferDepth]uint8
|
||||
xferQueue Queue
|
||||
xferTable dhwTransferLUT
|
||||
}
|
||||
@@ -761,7 +724,8 @@ func (s *dhwEPStatus) init(dhw *dhw, endpoint uint8) {
|
||||
s.endpoint = endpoint
|
||||
s.callback = nil
|
||||
s.flags.Set(0)
|
||||
s.xferQueue.Reset()
|
||||
fifo := s.xferFIFO[:]
|
||||
s.xferQueue.Init(&fifo, dhwTransferDepth, QueueFullDiscardLast)
|
||||
mps := dhw.endpointMaxPacketSize(endpoint)
|
||||
for i := range s.xferTable {
|
||||
s.xferTable[i].init(endpoint, mps)
|
||||
@@ -955,7 +919,6 @@ func (s *dhwEPStatus) scheduleTransfer(data uintptr, size uint32) (ready bool, o
|
||||
// returned for both return values.
|
||||
func (s *dhwEPStatus) scheduleSetup(setup dcdSetup) (ready bool, ok bool) {
|
||||
var i int
|
||||
s.device.logEvent("setup queued")
|
||||
if i, ok = s.claimSchedule(); ok {
|
||||
defer s.device.enableInterrupts(true)
|
||||
s.xferTable[i].reset()
|
||||
@@ -1157,14 +1120,18 @@ func (d *dhw) endpointEnable(endpoint uint8, control bool, config uint32) {
|
||||
|
||||
if enum, ok := endpointSizeEncode(descControlPacketSize); ok {
|
||||
|
||||
num := endpointNumber(d.controlEndpoint())
|
||||
|
||||
// Initialize IN and OUT transfer descriptors on control endpoint 0.
|
||||
d.ep[num][descDirRx].init(d, rxEndpoint(num))
|
||||
d.ep[num][descDirTx].init(d, txEndpoint(num))
|
||||
|
||||
// Conigure packet size for control endpoints.
|
||||
out.packetSize.ReplaceBits(enum,
|
||||
USB_DEVICE_PCKSIZE_SIZE_Msk, USB_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
in.packetSize.ReplaceBits(enum,
|
||||
USB_DEVICE_PCKSIZE_SIZE_Msk, USB_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
num := endpointNumber(d.controlEndpoint())
|
||||
|
||||
// rxType/txType uses the same rationale as epType (defined below in the
|
||||
// else-branch that handles non-control endpoints).
|
||||
// Thus, we add +1 to the value below.
|
||||
@@ -1193,11 +1160,15 @@ func (d *dhw) endpointEnable(endpoint uint8, control bool, config uint32) {
|
||||
|
||||
if enum, ok := endpointSizeEncode(d.endpointMaxPacketSize(endpoint)); ok {
|
||||
|
||||
num, dir := unpackEndpoint(endpoint)
|
||||
|
||||
// Initialize transfer descriptors now that the device class configuration
|
||||
// has been defined, which affects maximum packet size.
|
||||
d.ep[num][dir].init(d, endpoint)
|
||||
|
||||
desc.packetSize.ReplaceBits(enum,
|
||||
USB_DEVICE_PCKSIZE_SIZE_Msk, USB_DEVICE_PCKSIZE_SIZE_Pos)
|
||||
|
||||
num := endpointNumber(endpoint)
|
||||
|
||||
// config contains the bmAttributes field per USB standard EP descriptor,
|
||||
// i.e., ctrl=0, iso=1, bulk=2, int=3, which corresponds to the EPCFG
|
||||
// register's EPTYPE0/1 bitfield+1: ctrl=1, iso=2, bulk=3, int=4, dual=5.
|
||||
@@ -1217,9 +1188,8 @@ func (d *dhw) endpointEnable(endpoint uint8, control bool, config uint32) {
|
||||
sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_STALLRQ0 |
|
||||
sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_DTGLOUT)
|
||||
|
||||
d.bus.DEVICE_ENDPOINT[num].EPINTENSET.ReplaceBits(
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0,
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0_Msk, 0)
|
||||
d.bus.DEVICE_ENDPOINT[num].EPINTENSET.Set(
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0)
|
||||
|
||||
case txEndpoint(endpoint):
|
||||
|
||||
@@ -1234,15 +1204,16 @@ func (d *dhw) endpointEnable(endpoint uint8, control bool, config uint32) {
|
||||
sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_STALLRQ1 |
|
||||
sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_DTGLIN)
|
||||
|
||||
d.bus.DEVICE_ENDPOINT[num].EPINTENSET.ReplaceBits(
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1,
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1_Msk, 0)
|
||||
d.bus.DEVICE_ENDPOINT[num].EPINTENSET.Set(
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dhw) endpointConfigure(endpoint uint8) {
|
||||
func (d *dhw) endpointConfigure(endpoint uint8, callback func(endpoint uint8, size uint32)) {
|
||||
num, dir := unpackEndpoint(endpoint)
|
||||
d.ep[num][dir].callback = callback
|
||||
}
|
||||
|
||||
// endpointStall sets or clears a stall on the given endpoint.
|
||||
@@ -1299,7 +1270,7 @@ func (d *dhw) endpointTransfer(endpoint uint8, data uintptr, size uint32) {
|
||||
|
||||
switch num, dir := unpackEndpoint(endpoint); dir {
|
||||
|
||||
case descDirOut: // Rx
|
||||
case descDirRx: // OUT
|
||||
|
||||
// overwrite the BYTE_COUNT and MULTI_PACKET_SIZE bitfields only (with 0 and
|
||||
// size, respectively).
|
||||
@@ -1315,7 +1286,7 @@ func (d *dhw) endpointTransfer(endpoint uint8, data uintptr, size uint32) {
|
||||
d.bus.DEVICE_ENDPOINT[num].EPINTFLAG.SetBits(
|
||||
sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRFAIL0)
|
||||
|
||||
case descDirIn: // Tx
|
||||
case descDirTx: // IN
|
||||
|
||||
// overwrite the BYTE_COUNT and MULTI_PACKET_SIZE bitfields only (with size
|
||||
// and 0, respectively).
|
||||
@@ -1364,54 +1335,89 @@ func (d *dhw) uartConfigure() {
|
||||
acm := &descCDCACM[d.cc.config-1]
|
||||
|
||||
// SAMx51 only supports USB full-speed (FS) operation
|
||||
acm.sxSize = descCDCACMStatusFSPacketSize
|
||||
acm.rxSize = descCDCACMDataRxFSPacketSize
|
||||
acm.txSize = descCDCACMDataTxFSPacketSize
|
||||
|
||||
d.endpointEnable(descCDCACMEndpointStatus,
|
||||
rq := acm.rx[:]
|
||||
tq := acm.tx[:]
|
||||
|
||||
// Rx gives priority to incoming data, Tx gives priority to outgoing data
|
||||
acm.rq.Init(&rq, descCDCACMRxSize, QueueFullDiscardFirst)
|
||||
acm.tq.Init(&tq, descCDCACMTxSize, QueueFullDiscardLast)
|
||||
|
||||
d.endpointEnable(txEndpoint(descCDCACMEndpointStatus),
|
||||
false, descCDCACMConfigAttrStatus)
|
||||
d.endpointEnable(descCDCACMEndpointDataRx,
|
||||
d.endpointEnable(rxEndpoint(descCDCACMEndpointDataRx),
|
||||
false, descCDCACMConfigAttrDataRx)
|
||||
d.endpointEnable(descCDCACMEndpointDataTx,
|
||||
d.endpointEnable(txEndpoint(descCDCACMEndpointDataTx),
|
||||
false, descCDCACMConfigAttrDataTx)
|
||||
/*
|
||||
d.endpointConfigureTx(descCDCACMEndpointStatus,
|
||||
acm.sxSize, false, nil)
|
||||
d.endpointConfigureRx(descCDCACMEndpointDataRx,
|
||||
acm.rxSize, false, d.uartNotify)
|
||||
d.endpointConfigureTx(descCDCACMEndpointDataTx,
|
||||
acm.txSize, true, nil)
|
||||
|
||||
for i := range acm.rd {
|
||||
d.uartReceive(uint8(i))
|
||||
d.endpointConfigure(txEndpoint(descCDCACMEndpointStatus),
|
||||
nil)
|
||||
d.endpointConfigure(rxEndpoint(descCDCACMEndpointDataRx),
|
||||
d.uartReceiveComplete)
|
||||
d.endpointConfigure(txEndpoint(descCDCACMEndpointDataTx),
|
||||
nil)
|
||||
|
||||
d.uartReceive(descCDCACMEndpointDataRx)
|
||||
}
|
||||
|
||||
func (d *dhw) uartSetLineState(state uint16) {
|
||||
acm := &descCDCACM[d.cc.config-1]
|
||||
if acm.ls.parse(state) {
|
||||
// TBD: respond to changes in line state?
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dhw) uartSetLineCoding(coding []uint8) {
|
||||
acm := &descCDCACM[d.cc.config-1]
|
||||
if acm.lc.parse(coding) {
|
||||
switch acm.lc.baud {
|
||||
case 1200:
|
||||
if acm.ls.dataTerminalReady {
|
||||
// reboot CPU
|
||||
}
|
||||
}
|
||||
*/
|
||||
d.timerConfigure(0, descCDCACMTxSyncUs, d.uartSync)
|
||||
}
|
||||
|
||||
func (d *dhw) uartSetLineState(dtr, rts bool) {
|
||||
}
|
||||
|
||||
func (d *dhw) uartSetLineCoding(coding descCDCACMLineCoding) {
|
||||
if 134 == coding.baud {
|
||||
d.enableSOF(true, descCDCACMInterfaceCount)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dhw) uartReady() bool {
|
||||
acm := &descCDCACM[d.cc.config-1]
|
||||
_ = acm // TODO(ardnew): elaborate stub
|
||||
return false
|
||||
return d.dcd.state() == dcdStateConfigured
|
||||
}
|
||||
|
||||
func (d *dhw) uartReceive(endpoint uint8) {
|
||||
acm := &descCDCACM[d.cc.config-1]
|
||||
num := uint16(endpoint) & descEndptAddrNumberMsk
|
||||
_, _ = acm, num // TODO(ardnew): elaborate stub
|
||||
|
||||
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)
|
||||
next := xfer.packetStart(xfer.data, xfer.size)
|
||||
d.endpointTransfer(endpoint, xfer.data, next)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dhw) uartNotify(endpoint uint8, size uint32) {
|
||||
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++ {
|
||||
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)
|
||||
}
|
||||
|
||||
// uartFlush discards all buffered input (Rx) data.
|
||||
@@ -1445,7 +1451,7 @@ func (d *dhw) uartRead(data []uint8) int {
|
||||
}
|
||||
|
||||
func (d *dhw) uartWriteByte(c uint8) bool {
|
||||
return 1 == d.uartWrite([]uint8{c})
|
||||
return d.uartWrite([]uint8{c}) == 1
|
||||
}
|
||||
|
||||
func (d *dhw) uartWrite(data []uint8) int {
|
||||
|
||||
+212
-54
@@ -5,12 +5,19 @@ import (
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
const QueueSize = 8
|
||||
type QueueFullDiscardMode uint8
|
||||
|
||||
const (
|
||||
QueueFullDiscardLast QueueFullDiscardMode = iota // Drop incoming data
|
||||
QueueFullDiscardFirst // Drop outgoing data
|
||||
)
|
||||
|
||||
type Queue struct {
|
||||
fifo [QueueSize]uint8
|
||||
tail volatile.Register32
|
||||
head volatile.Register32
|
||||
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 (
|
||||
@@ -18,28 +25,94 @@ var (
|
||||
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")
|
||||
)
|
||||
|
||||
// Reset discards all buffered data.
|
||||
// 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() {
|
||||
for i := range q.fifo {
|
||||
q.fifo[i] = 0
|
||||
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) Cap() int {
|
||||
return cap(q.fifo)
|
||||
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))
|
||||
@@ -59,7 +132,7 @@ func (q *Queue) Read(data []uint8) (int, error) {
|
||||
} // only get from used space
|
||||
|
||||
for i := uint32(0); i < less; i++ {
|
||||
data[i] = q.fifo[head%QueueSize]
|
||||
data[i] = (*q.fifo)[head%q.size.Get()]
|
||||
head++
|
||||
}
|
||||
q.head.Set(head)
|
||||
@@ -67,65 +140,150 @@ func (q *Queue) Read(data []uint8) (int, error) {
|
||||
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, ErrWriteBuffer
|
||||
} // nothing to copy from
|
||||
|
||||
tail := q.tail.Get()
|
||||
used := tail - q.head.Get()
|
||||
|
||||
if used == QueueSize {
|
||||
return 0, ErrQueueFull
|
||||
} // full queue
|
||||
|
||||
if used+more > QueueSize {
|
||||
more = QueueSize - used
|
||||
} // only put to unused space
|
||||
|
||||
for i := uint32(0); i < more; i++ {
|
||||
q.fifo[tail%QueueSize] = data[i]
|
||||
tail++
|
||||
}
|
||||
q.tail.Set(tail)
|
||||
|
||||
return int(more), nil
|
||||
}
|
||||
|
||||
func (q *Queue) Deq() (uint8, bool) {
|
||||
|
||||
tail := q.head.Get()
|
||||
if tail == q.tail.Get() {
|
||||
return 0, false
|
||||
} // empty queue
|
||||
|
||||
data := q.fifo[tail%QueueSize]
|
||||
q.head.Set(tail + 1)
|
||||
|
||||
return data, true
|
||||
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
|
||||
}
|
||||
|
||||
// xOnly 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[i]
|
||||
tail++
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
tail := q.head.Get()
|
||||
if tail == q.tail.Get() {
|
||||
head := q.head.Get()
|
||||
if head == q.tail.Get() {
|
||||
return 0, false
|
||||
} // empty queue
|
||||
|
||||
return q.fifo[tail%QueueSize], true
|
||||
return (*q.fifo)[head%q.size.Get()], true
|
||||
}
|
||||
|
||||
func (q *Queue) Enq(data uint8) bool {
|
||||
// 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) {
|
||||
|
||||
head := q.tail.Get()
|
||||
if head-q.head.Get() == QueueSize {
|
||||
return false
|
||||
} // full queue
|
||||
tail := q.tail.Get()
|
||||
if tail == q.head.Get() {
|
||||
return 0, false
|
||||
} // empty queue
|
||||
|
||||
q.fifo[head%QueueSize] = data
|
||||
q.tail.Set(head + 1)
|
||||
|
||||
return true
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user