From 5f6489d3cfab1ac07a513ccb100420b6dc016428 Mon Sep 17 00:00:00 2001 From: ardnew Date: Fri, 18 Feb 2022 12:09:04 -0600 Subject: [PATCH] add basic CDC-ACM UART Rx capability --- src/machine/usb/dcd.go | 15 +- src/machine/usb/desc.go | 52 +++++- src/machine/usb/desc_atsamd51.go | 35 +++- src/machine/usb/dhw_atsamd51.go | 210 ++++++++++++------------ src/machine/usb/queue.go | 266 ++++++++++++++++++++++++------- 5 files changed, 396 insertions(+), 182 deletions(-) diff --git a/src/machine/usb/dcd.go b/src/machine/usb/dcd.go index 0b5aa5994..630cbd9d6 100644 --- a/src/machine/usb/dcd.go +++ b/src/machine/usb/dcd.go @@ -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 diff --git a/src/machine/usb/desc.go b/src/machine/usb/desc.go index 0ca06222d..c417939f6 100644 --- a/src/machine/usb/desc.go +++ b/src/machine/usb/desc.go @@ -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 diff --git a/src/machine/usb/desc_atsamd51.go b/src/machine/usb/desc_atsamd51.go index 24f9ca1f2..5f0bf6b84 100644 --- a/src/machine/usb/desc_atsamd51.go +++ b/src/machine/usb/desc_atsamd51.go @@ -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, diff --git a/src/machine/usb/dhw_atsamd51.go b/src/machine/usb/dhw_atsamd51.go index cc46ec5a9..90b77ee5f 100644 --- a/src/machine/usb/dhw_atsamd51.go +++ b/src/machine/usb/dhw_atsamd51.go @@ -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 { diff --git a/src/machine/usb/queue.go b/src/machine/usb/queue.go index c4270732f..074d58265 100644 --- a/src/machine/usb/queue.go +++ b/src/machine/usb/queue.go @@ -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 }