USB CDC-ACM UART Rx/Tx functioning for baseline target (Teensy 4.0/4.1)

This commit is contained in:
ardnew
2021-04-20 16:35:59 -05:00
committed by sago35
parent 8103b3c3c8
commit 69e8257e7b
11 changed files with 453 additions and 242 deletions
+2 -3
View File
@@ -111,9 +111,8 @@ func init() {
var ( var (
// USBCDC is a legacy class being retained here as temporary wrapper. // USBCDC is a legacy class being retained here as temporary wrapper.
// See godoc comments on type USBCDC struct definition for details. // See godoc comments on type USBCDC struct definition for details.
USBCDC0 = USBCDC{ UART0 = USBCDC{
port: 0, // USB_OTG1 (Micro-B port on Teensy 4.0) port: 0, // USB_OTG1 (Micro-B port on Teensy 4.0/4.1)
buff: NewRingBuffer(),
} }
) )
+26 -6
View File
@@ -1,10 +1,5 @@
// +build mimxrt1062 // +build mimxrt1062
// Compatibility wrapper for legacy type USBCDC, which provides USB CDC-ACM
// device class emulation for serial UART communication.
// This functionality is being replaced by a platform-agnostic type usb.UART in
// package "machine/usb".
package machine package machine
import ( import (
@@ -18,7 +13,6 @@ import (
// be removed and usb.UART should be used directly instead. // be removed and usb.UART should be used directly instead.
type USBCDC struct { type USBCDC struct {
port uint8 port uint8
buff *RingBuffer
uart usb2.UART uart usb2.UART
} }
@@ -27,3 +21,29 @@ type USBCDC struct {
func (cdc *USBCDC) Configure(config UARTConfig) { func (cdc *USBCDC) Configure(config UARTConfig) {
cdc.uart.Configure(usb2.UARTConfig{BaudRate: config.BaudRate}) cdc.uart.Configure(usb2.UARTConfig{BaudRate: config.BaudRate})
} }
// Buffered returns the number of bytes currently stored in the RX buffer.
func (cdc USBCDC) Buffered() int {
return cdc.uart.Buffered()
}
// ReadByte reads a single byte from the RX buffer.
// If there is no data in the buffer, returns an error.
func (cdc USBCDC) ReadByte() (byte, error) {
return cdc.uart.ReadByte()
}
// Read from the RX buffer.
func (cdc USBCDC) Read(data []byte) (n int, err error) {
return cdc.uart.Read(data)
}
// WriteByte writes a single byte of data to the UART interface.
func (cdc USBCDC) WriteByte(c byte) error {
return cdc.uart.WriteByte(c)
}
// Write data to the UART.
func (cdc USBCDC) Write(data []byte) (n int, err error) {
return cdc.uart.Write(data)
}
+1
View File
@@ -3,6 +3,7 @@ package usb2
import "unsafe" import "unsafe"
type dcd interface { type dcd interface {
class() class
init() status init() status
enable(enable bool) status enable(enable bool) status
critical(enter bool) status critical(enter bool) status
+241 -152
View File
@@ -22,10 +22,10 @@ const dcdInterruptPriority = 3
// deviceController implements USB device controller driver (dcd) interface. // deviceController implements USB device controller driver (dcd) interface.
type deviceController struct { type deviceController struct {
core *core // Parent USB core this instance is attached to core *core // Parent USB core this instance is attached to
port int // USB port index port int // USB port index
class class // USB device class cc class // USB device class
id int // deviceControllerInstance index id int // deviceControllerInstance index
bus *nxp.USB_Type bus *nxp.USB_Type
phy *nxp.USBPHY_Type phy *nxp.USBPHY_Type
@@ -37,8 +37,6 @@ type deviceController struct {
stat *dcdEndpoint // endpoint 0 Rx ("out" direction) stat *dcdEndpoint // endpoint 0 Rx ("out" direction)
ctrl *dcdEndpoint // endpoint 0 Tx ("in" direction) ctrl *dcdEndpoint // endpoint 0 Tx ("in" direction)
acm *descCDCACMClass
timerInterrupt [2]func() timerInterrupt [2]func()
controlNotify uint32 controlNotify uint32
endpointNotify uint32 endpointNotify uint32
@@ -84,7 +82,7 @@ func initDCD(port int, class class) (dcd, status) {
// Initialize device controller. // Initialize device controller.
deviceControllerInstance[i].core = &coreInstance[port] deviceControllerInstance[i].core = &coreInstance[port]
deviceControllerInstance[i].port = port deviceControllerInstance[i].port = port
deviceControllerInstance[i].class = class deviceControllerInstance[i].cc = class
deviceControllerInstance[i].id = i deviceControllerInstance[i].id = i
switch port { switch port {
case 0: case 0:
@@ -105,17 +103,14 @@ func initDCD(port int, class class) (dcd, status) {
//coreInstance[1].dc.interrupt() //coreInstance[1].dc.interrupt()
}) })
} }
switch class.id {
case classDeviceCDCACM:
deviceControllerInstance[i].acm = &descCDCACM[class.config-1]
default:
}
return &deviceControllerInstance[i], statusOK return &deviceControllerInstance[i], statusOK
} }
} }
return nil, statusBusy // No free device controller instances available. return nil, statusBusy // No free device controller instances available.
} }
func (dc *deviceController) class() class { return dc.cc }
func (dc *deviceController) init() status { func (dc *deviceController) init() status {
// reset the controller // reset the controller
dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST) dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST)
@@ -277,7 +272,7 @@ func (dc *deviceController) interrupt() {
dc.bus.ENDPTFLUSH.Set(0xFFFFFFFF) dc.bus.ENDPTFLUSH.Set(0xFFFFFFFF)
// if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR) { // if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR) {
// } // }
switch dc.class.id { switch dc.cc.id {
case classDeviceCDCACM: case classDeviceCDCACM:
// TBD: reset CDC-ACM UART? // TBD: reset CDC-ACM UART?
default: default:
@@ -431,21 +426,21 @@ func (dc *deviceController) control(setup dcdSetup) {
// SET CONFIGURATION (0x09): // SET CONFIGURATION (0x09):
case descRequestStandardSetConfiguration: case descRequestStandardSetConfiguration:
dc.class.config = int(setup.wValue) dc.cc.config = int(setup.wValue)
if 0 == dc.class.config || dc.class.config > dcdCount { if 0 == dc.cc.config || dc.cc.config > dcdCount {
// Use default if invalid index received // Use default if invalid index received
dc.class.config = 1 dc.cc.config = 1
} }
// Respond based on our device class configuration // Respond based on our device class configuration
switch dc.class.id { switch dc.cc.id {
// CDC-ACM (single) // CDC-ACM (single)
case classDeviceCDCACM: case classDeviceCDCACM:
dc.bus.ENDPTCTRL2.Set(descCDCACMConfigAttrStatus) // Status Tx dc.bus.ENDPTCTRL2.Set(descCDCACMConfigAttrStatus) // Status Tx
dc.bus.ENDPTCTRL3.Set(descCDCACMConfigAttrDataRx) // Bulk data Rx dc.bus.ENDPTCTRL3.Set(descCDCACMConfigAttrDataRx) // Bulk data Rx
dc.bus.ENDPTCTRL4.Set(descCDCACMConfigAttrDataTx) // Bulk data Tx dc.bus.ENDPTCTRL4.Set(descCDCACMConfigAttrDataTx) // Bulk data Tx
dc.serialConfigure() dc.uartConfigure()
dc.controlReceive(dcdPointerNil, 0, false) dc.controlReceive(dcdPointerNil, 0, false)
default: default:
@@ -478,7 +473,7 @@ func (dc *deviceController) control(setup dcdSetup) {
// GET CONFIGURATION (0x08): // GET CONFIGURATION (0x08):
case descRequestStandardGetConfiguration: case descRequestStandardGetConfiguration:
dc.controlReply[0] = uint8(dc.class.config) dc.controlReply[0] = uint8(dc.cc.config)
dc.controlTransmit( dc.controlTransmit(
uintptr(unsafe.Pointer(&dc.controlReply[0])), 1, false) uintptr(unsafe.Pointer(&dc.controlReply[0])), 1, false)
return return
@@ -585,7 +580,7 @@ func (dc *deviceController) control(setup dcdSetup) {
case descCDCRequestSetLineCoding: case descCDCRequestSetLineCoding:
// Respond based on our device class configuration // Respond based on our device class configuration
switch dc.class.id { switch dc.cc.id {
// CDC-ACM (single) // CDC-ACM (single)
case classDeviceCDCACM: case classDeviceCDCACM:
@@ -593,7 +588,7 @@ func (dc *deviceController) control(setup dcdSetup) {
if descCDCACMCodingSize == setup.wLength { if descCDCACMCodingSize == setup.wLength {
dc.setup = setup dc.setup = setup
dc.controlReceive( dc.controlReceive(
uintptr(unsafe.Pointer(&descCDCACM[dc.class.config-1].cx[0])), uintptr(unsafe.Pointer(&descCDCACM[dc.cc.config-1].cx[0])),
descCDCACMCodingSize, true) descCDCACMCodingSize, true)
return return
} }
@@ -606,7 +601,7 @@ func (dc *deviceController) control(setup dcdSetup) {
case descCDCRequestSetControlLineState: case descCDCRequestSetControlLineState:
// Respond based on our device class configuration // Respond based on our device class configuration
switch dc.class.id { switch dc.cc.id {
// CDC-ACM (single) // CDC-ACM (single)
case classDeviceCDCACM: case classDeviceCDCACM:
@@ -616,9 +611,11 @@ func (dc *deviceController) control(setup dcdSetup) {
// Control/status interface: // Control/status interface:
case descCDCACMInterfaceCtrl: case descCDCACMInterfaceCtrl:
acm := &descCDCACM[dc.class.config-1] // acm := &descCDCACM[dc.cc.config-1]
acm.cticks = ticks() // update our emulated UART terminal status
acm.rtsdtr = uint8(setup.wValue) // acm.lineActive = ticks()
// acm.lineCoding.dtr = 0 != setup.wValue&0x01
// acm.lineCoding.rts = 0 != setup.wValue&0x02
dc.controlReceive(dcdPointerNil, 0, false) dc.controlReceive(dcdPointerNil, 0, false)
return return
@@ -634,7 +631,7 @@ func (dc *deviceController) control(setup dcdSetup) {
case descCDCRequestSendBreak: case descCDCRequestSendBreak:
// Respond based on our device class configuration // Respond based on our device class configuration
switch dc.class.id { switch dc.cc.id {
// CDC-ACM (single) // CDC-ACM (single)
case classDeviceCDCACM: case classDeviceCDCACM:
@@ -666,9 +663,9 @@ func (dc *deviceController) control(setup dcdSetup) {
//go:inline //go:inline
func (dc *deviceController) controlTransfers() (dat, ack *dcdTransfer) { func (dc *deviceController) controlTransfers() (dat, ack *dcdTransfer) {
// control endpoint is device class-specific // control endpoint is device class-specific
switch dc.class.id { switch dc.cc.id {
case classDeviceCDCACM: case classDeviceCDCACM:
return descCDCACM[dc.class.config-1].cd, descCDCACM[dc.class.config-1].ad return descCDCACM[dc.cc.config-1].cd, descCDCACM[dc.cc.config-1].ad
default: default:
return nil, nil return nil, nil
} }
@@ -677,11 +674,11 @@ func (dc *deviceController) controlTransfers() (dat, ack *dcdTransfer) {
func (dc *deviceController) controlDescriptor(setup dcdSetup) { func (dc *deviceController) controlDescriptor(setup dcdSetup) {
// Respond based on our device class configuration // Respond based on our device class configuration
switch dc.class.id { switch dc.cc.id {
// CDC-ACM (single) // CDC-ACM (single)
case classDeviceCDCACM: case classDeviceCDCACM:
acm := &descCDCACM[dc.class.config-1] acm := &descCDCACM[dc.cc.config-1]
dxn := uint8(0) dxn := uint8(0)
// Determine the type of descriptor being requested // Determine the type of descriptor being requested
@@ -699,64 +696,61 @@ func (dc *deviceController) controlDescriptor(setup dcdSetup) {
// String descriptor // String descriptor
case descTypeString: case descTypeString:
var sd []uint8
if 0 == uint8(setup.wValue) {
// setup.wIndex contains an arbitrary index referring to a collection of
// strings in some given language. This (setup.wValue = 0x03[00]) is a
// request from the host to determine what that language is. Subsequent
// string requests will populate setup.wIndex with the language code
// returned here in this string descriptor.
sd = acm.locale[int(setup.wIndex)].descriptor[setup.wValue&0xFF][:]
} else {
// setup.wIndex now contains a language code, which we notified in a
// previous request (above: setup.wValue = 0x03[00]). We need to locate
// the set of strings whose language matches the language code given in
// this new setup.wIndex.
for code := range acm.locale {
if setup.wIndex == acm.locale[code].language {
// Found language, check if string descriptor at given index exists
if int(setup.wValue&0xFF) < len(acm.locale[code].descriptor) {
// Found language with a string defined at the requested index.
// Construct a string descriptor dynamically to be transmitted on
// the serial bus.
var s []uint8 // TODO: Add fields to deviceController and design an API that
// allows the user to define and provide these strings
// prior to deviceController initialization.
// For now, we just always use the descCommon* strings.
var s string
switch uint8(setup.wValue) {
case 1:
s = descCommonManufacturer
case 2:
s = descCommonProduct
case 3:
s = descCommonSerialNumber
}
// Determine the string index requested // Copy string into string descriptor as UTF-16
switch uint8(setup.wValue) { sd = acm.locale[code].descriptor[int(setup.wValue&0xFF)][:]
sd[0] = uint8(2 + 2*len(s))
// Language sd[1] = descTypeString
case 0: for n, c := range s {
if int(setup.wIndex) < len(acm.locstr) { if 2+2*n >= len(sd) {
s = acm.locstr[setup.wIndex].index[0][:] break
} }
sd[2+2*n] = uint8(c)
// Manufacturer sd[3+2*n] = 0
case 1: }
for i := range acm.locstr { break // end search for matching language code
if acm.locstr[i].language == setup.wIndex {
s = acm.locstr[i].index[1][:]
// copy manufacturer string to uint8 buffer as UTF-16
for n, c := range descManufacturer {
s[2+2*n] = uint8(c)
s[3+2*n] = 0
} }
break
}
}
// Product
case 2:
for i := range acm.locstr {
if acm.locstr[i].language == setup.wIndex {
s = acm.locstr[i].index[2][:]
// copy product string to uint8 buffer as UTF-16
for n, c := range descProduct {
s[2+2*n] = uint8(c)
s[3+2*n] = 0
}
break
}
}
// Serial number
case 3:
for i := range acm.locstr {
if acm.locstr[i].language == setup.wIndex {
s = acm.locstr[i].index[3][:]
// copy serial number string to uint8 buffer as UTF-16
for n, c := range descSerialNumber {
s[2+2*n] = uint8(c)
s[3+2*n] = 0
}
break
} }
} }
} }
// Copy string descriptor into descriptor transmit buffer
if nil != s && len(s) > 0 { if nil != sd && len(sd) >= 0 {
dxn = s[0] dxn = sd[0]
_ = copy(acm.dx[:], s[:dxn]) _ = copy(acm.dx[:], sd[:dxn])
} }
// Device qualification descriptor // Device qualification descriptor
@@ -766,7 +760,10 @@ func (dc *deviceController) controlDescriptor(setup dcdSetup) {
// Alternate configuration descriptor // Alternate configuration descriptor
case descTypeOtherSpeedConfiguration: case descTypeOtherSpeedConfiguration:
// TODO
default:
// Unhandled descriptor type
} }
if dxn > 0 { if dxn > 0 {
@@ -884,23 +881,19 @@ func (dc *deviceController) controlComplete() {
case descCDCRequestSetLineCoding: case descCDCRequestSetLineCoding:
// Respond based on our device class configuration // Respond based on our device class configuration
switch dc.class.id { switch dc.cc.id {
// CDC-ACM (single) // CDC-ACM (single)
case classDeviceCDCACM: case classDeviceCDCACM:
acm := &descCDCACM[dc.cc.config-1]
// Determine interface destination of the notification // Determine interface destination of the notification
switch dc.setup.wIndex { switch dc.setup.wIndex {
// Control/status interface: // Control/status interface:
case descCDCACMInterfaceCtrl: case descCDCACMInterfaceCtrl:
if acm.lineCoding.parse(acm.cx[:]) {
_ = copy(descCDCACM[dc.class.config-1].coding[:], if 134 == acm.lineCoding.baud {
// descCDCACM[dc.class.config-1].costat[:descCDCACMCodingSize])
descCDCACM[dc.class.config-1].cx[:])
var coding descCDCACMLineCoding
if coding.parse(descCDCACM[dc.class.config-1].coding[:]) {
if 134 == coding.baud {
dc.enableSofInterrupts(true, descCDCACMInterfaceCount) dc.enableSofInterrupts(true, descCDCACMInterfaceCount)
dc.rebootTimer = 80 dc.rebootTimer = 80
} }
@@ -925,46 +918,6 @@ func (dc *deviceController) controlComplete() {
default: default:
// Unhandled request type // Unhandled request type
} }
// // determine interface destination of the notification
// switch dc.setup.wIndex {
// // communication/control interface:
// case descCDCACMInterfaceCtrl:
// // switch on the type and recepient of the request
// switch dc.setup.bmRequestType &
// (descRequestTypeTypeMsk | descRequestTypeRecipientMsk) {
// // interface class request:
// case descRequestTypeRecipientInterface | descRequestTypeTypeClass:
// // identify which request was received
// switch dc.setup.bRequest {
// // CDC_SET_LINE_CODING:
// case descCDCRequestSetLineCoding:
// // respond according to our device class
// switch dc.class.id {
// // CDC-ACM (single)
// case classDeviceCDCACM:
// _ = copy(descCDCACM[dc.class.config-1].coding[:],
// // descCDCACM[dc.class.config-1].costat[:descCDCACMCodingSize])
// descCDCACM[dc.class.config-1].cx[:])
// var coding descCDCACMLineCoding
// if coding.parse(descCDCACM[dc.class.config-1].coding[:]) {
// if 134 == coding.baud {
// dc.enableSofInterrupts(true, descCDCACMInterfaceCount)
// dc.rebootTimer = 80
// }
// }
// default:
// // unhandled device class
// }
// default:
// // unhandled request
// }
// default:
// // unhandled request type or recepient
// }
// default:
// // unhandled interface
// }
} }
// endpointQueueHead returns the queue head for the given endpoint address, // endpointQueueHead returns the queue head for the given endpoint address,
@@ -972,9 +925,9 @@ func (dc *deviceController) controlComplete() {
//go:inline //go:inline
func (dc *deviceController) endpointQueueHead(endpoint uint8) *dcdEndpoint { func (dc *deviceController) endpointQueueHead(endpoint uint8) *dcdEndpoint {
// endpoint queue head is device class-specific // endpoint queue head is device class-specific
switch dc.class.id { switch dc.cc.id {
case classDeviceCDCACM: case classDeviceCDCACM:
return &descCDCACM[dc.class.config-1].qh[endpointIndex(endpoint)] return &descCDCACM[dc.cc.config-1].qh[endpointIndex(endpoint)]
default: default:
return nil return nil
} }
@@ -1144,8 +1097,8 @@ func (dc *deviceController) timerStop(timer int) {
} }
} }
func (dc *deviceController) serialConfigure() { func (dc *deviceController) uartConfigure() {
acm := &descCDCACM[dc.class.config-1] acm := &descCDCACM[dc.cc.config-1]
switch dc.speed { switch dc.speed {
case descDeviceSpeedHigh: case descDeviceSpeedHigh:
acm.rxSize = descCDCACMDataRxHSPacketSize acm.rxSize = descCDCACMDataRxHSPacketSize
@@ -1162,22 +1115,33 @@ func (dc *deviceController) serialConfigure() {
dc.endpointConfigureTx(descCDCACMEndpointStatus, dc.endpointConfigureTx(descCDCACMEndpointStatus,
acm.cxSize, false, nil) acm.cxSize, false, nil)
dc.endpointConfigureRx(descCDCACMEndpointDataRx, dc.endpointConfigureRx(descCDCACMEndpointDataRx,
acm.rxSize, false, dc.serialNotify) acm.rxSize, false, dc.uartNotify)
dc.endpointConfigureTx(descCDCACMEndpointDataTx, dc.endpointConfigureTx(descCDCACMEndpointDataTx,
acm.txSize, true, nil) acm.txSize, true, nil)
for i := range acm.rd { for i := range acm.rd {
dc.serialReceive(uint8(i)) dc.uartReceive(uint8(i))
} }
dc.timerConfigure(0, 75, dc.serialFlush) dc.timerConfigure(0, descCDCACMTxSyncUs, dc.uartSync)
} }
func (dc *deviceController) serialNotify(transfer *dcdTransfer) { func (dc *deviceController) uartReceive(endpoint uint8) {
acm := &descCDCACM[dc.class.config-1] acm := &descCDCACM[dc.cc.config-1]
num := uint16(endpoint) & descEndptAddrNumberMsk
buf := &acm.rx[num*descCDCACMRxSize]
dc.irq.Disable()
dc.transferPrepare(&acm.rd[num], buf, acm.rxSize, uint32(endpoint))
nxp.DeleteDcache(uintptr(unsafe.Pointer(buf)), uintptr(acm.rxSize))
dc.receive(descCDCACMEndpointDataRx, &acm.rd[num])
dc.irq.Enable()
}
func (dc *deviceController) uartNotify(transfer *dcdTransfer) {
acm := &descCDCACM[dc.cc.config-1]
len := acm.rxSize - (uint16(transfer.token>>16) & 0x7FFF) len := acm.rxSize - (uint16(transfer.token>>16) & 0x7FFF)
p := transfer.param p := transfer.param
if 0 == len { if 0 == len {
// zero-length packet (ZLP) // zero-length packet (ZLP)
dc.serialReceive(uint8(p)) dc.uartReceive(uint8(p))
} else { } else {
// data packet // data packet
h := acm.rxHead h := acm.rxHead
@@ -1191,7 +1155,7 @@ func (dc *deviceController) serialNotify(transfer *dcdTransfer) {
acm.rx[p*descCDCACMRxSize:uint16(p)*descCDCACMRxSize+len]) acm.rx[p*descCDCACMRxSize:uint16(p)*descCDCACMRxSize+len])
acm.rxCount[q] = n + len acm.rxCount[q] = n + len
acm.rxFree += len acm.rxFree += len
dc.serialReceive(uint8(p)) dc.uartReceive(uint8(p))
return return
} }
} }
@@ -1208,23 +1172,148 @@ func (dc *deviceController) serialNotify(transfer *dcdTransfer) {
} }
} }
func (dc *deviceController) serialReceive(endpoint uint8) { // uartFlush discards all buffered input (Rx) data.
ivm := arm.DisableInterrupts() func (dc *deviceController) uartFlush() {
num := uint16(endpoint) & descEndptAddrNumberMsk acm := &descCDCACM[dc.cc.config-1]
acm := &descCDCACM[dc.class.config-1] tail := acm.rxTail
buf := &acm.rx[num*descCDCACMRxSize] for tail != acm.rxHead {
dc.transferPrepare(&acm.rd[num], buf, acm.rxSize, uint32(endpoint)) tail += 1
nxp.DeleteDcache(uintptr(unsafe.Pointer(buf)), uintptr(acm.rxSize)) if tail > descCDCACMRDCount {
dc.receive(descCDCACMEndpointDataRx, &acm.rd[num]) tail = 0
arm.EnableInterrupts(ivm) }
i := acm.rxQueue[tail]
acm.rxFree -= acm.rxCount[i] - acm.rxIndex[i]
dc.uartReceive(uint8(i))
acm.rxTail = tail
}
} }
func (dc *deviceController) serialFlush() { func (dc *deviceController) uartAvailable() int {
return int(descCDCACM[dc.cc.config-1].rxFree)
}
func (dc *deviceController) uartPeek() (uint8, bool) {
acm := &descCDCACM[dc.cc.config-1]
tail := acm.rxTail
if tail == acm.rxHead {
return 0, false
}
tail += 1
if tail > descCDCACMRDCount {
tail = 0
}
i := acm.rxQueue[tail]
return acm.rx[i*descCDCACMRxSize+acm.rxIndex[i]], true
}
func (dc *deviceController) uartReadByte() (uint8, bool) {
b := []uint8{0}
ok := dc.uartRead(b) > 0
return b[0], ok
}
func (dc *deviceController) uartRead(data []uint8) int {
acm := &descCDCACM[dc.cc.config-1]
read := uint16(0)
size := uint16(len(data))
tail := acm.rxTail
dest := uint16(0)
dc.irq.Disable()
for read < size && tail != acm.rxHead {
tail += 1
if tail > descCDCACMRDCount {
tail = 0
}
i := acm.rxQueue[tail]
count := uint16(size - read)
avail := acm.rxCount[i] - acm.rxIndex[i]
start := i*descCDCACMRxSize + acm.rxIndex[i]
if avail > count {
// partially consume packet
_ = copy(data[dest:], acm.rx[start:start+count])
acm.rxFree -= count
acm.rxIndex[i] += count
read += count
} else {
// fully consume packet
_ = copy(data[dest:], acm.rx[start:start+avail])
dest += avail //* uint16(unsafe.Sizeof(&data[0]))
read += avail
acm.rxFree -= avail
acm.rxTail = tail
dc.uartReceive(uint8(i))
}
}
dc.irq.Enable()
return int(read)
}
func (dc *deviceController) uartWriteByte(c uint8) bool {
return 1 == dc.uartWrite([]uint8{c})
}
func (dc *deviceController) uartWrite(data []uint8) int {
acm := &descCDCACM[dc.cc.config-1]
sent := 0
size := len(data)
for size > 0 {
xfer := &acm.td[acm.txHead]
wait := false
when := int64(0)
for 0 == acm.txFree {
if 0 == xfer.token&0x80 {
if 0 != xfer.token&0x68 {
// TODO: token contains error, how to handle?
}
acm.txFree = descCDCACMTxSize
acm.txPrev = false
break
}
if !wait {
wait = true
when = ticks()
}
if acm.txPrev {
return sent
}
if ticks()-when > descCDCACMTxTimeoutMs {
acm.txPrev = true
return sent
}
}
buff := acm.tx[(int(acm.txHead)*descCDCACMTxSize)+
(descCDCACMTxSize-int(acm.txFree)):]
if size > int(acm.txFree) {
_ = copy(buff, data[sent:sent+int(acm.txFree)])
tx := &acm.tx[int(acm.txHead)*descCDCACMTxSize]
dc.transferPrepare(xfer, tx, descCDCACMTxSize, 0)
nxp.FlushDeleteDcache(uintptr(unsafe.Pointer(tx)), descCDCACMTxSize)
dc.transmit(descCDCACMEndpointDataTx, xfer)
acm.txHead += 1
if acm.txHead >= descCDCACMTDCount {
acm.txHead = 0
}
size -= int(acm.txFree)
sent += int(acm.txFree)
acm.txFree = 0
dc.timerStop(0)
} else {
_ = copy(buff, data[:size])
acm.txFree -= uint16(size)
sent += size
size = 0
dc.timerOneShot(0)
}
}
return sent
}
func (dc *deviceController) uartSync() {
const autoFlushTx = true const autoFlushTx = true
if !autoFlushTx { if !autoFlushTx {
return return
} }
acm := &descCDCACM[dc.class.config-1] acm := &descCDCACM[dc.cc.config-1]
if 0 == acm.txFree { if 0 == acm.txFree {
return return
} }
+70 -58
View File
@@ -4,12 +4,6 @@ const descUSBSpecVersion = uint16(0x0200) // USB 2.0
const descLanguageEnglish = uint16(0x0409) const descLanguageEnglish = uint16(0x0409)
type descIndexStrings [4][64]uint8
type descLocalStrings struct {
language uint16
index descIndexStrings // UTF-16, 32-character maximum length
}
// USB constants defined per specification. // USB constants defined per specification.
const ( const (
@@ -335,55 +329,27 @@ const (
descCDCACMConfigAttrInterrupt = descCDCACMConfigAttr | descEndptAttrSyncTypeSync descCDCACMConfigAttrInterrupt = descCDCACMConfigAttr | descEndptAttrSyncTypeSync
) )
// descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e.,
// configuration index 1.
var descCDCACM0String = [descCDCACMLanguageCount]descLocalStrings{
{
language: descLanguageEnglish,
index: descIndexStrings{
{ // 0: language string
4,
descTypeString,
lsU8(descLanguageEnglish),
msU8(descLanguageEnglish),
},
{ // 1: manufacturer
uint8(2 + 2*len(descManufacturer)),
descTypeString,
},
{ // 2: product
uint8(2 + 2*len(descProduct)),
descTypeString,
},
{ // 3: serial number
uint8(2 + 2*len(descSerialNumber)),
descTypeString,
},
},
},
}
// descCDCACM0Device holds the default device descriptor for CDC-ACM[0], i.e., // descCDCACM0Device holds the default device descriptor for CDC-ACM[0], i.e.,
// configuration index 1. // configuration index 1.
var descCDCACM0Device = [descLengthDevice]uint8{ var descCDCACM0Device = [descLengthDevice]uint8{
descLengthDevice, // Size of this descriptor in bytes descLengthDevice, // Size of this descriptor in bytes
descTypeDevice, // Descriptor Type descTypeDevice, // Descriptor Type
lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low) lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low)
msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high) msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high)
descCDCTypeComm, // Class code (assigned by the USB-IF). descCDCTypeComm, // Class code (assigned by the USB-IF).
descCDCSubNone, // Subclass code (assigned by the USB-IF). descCDCSubNone, // Subclass code (assigned by the USB-IF).
descCDCProtoNone, // Protocol code (assigned by the USB-IF). descCDCProtoNone, // Protocol code (assigned by the USB-IF).
descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64) descEndptMaxPktSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
lsU8(descVendorID), // Vendor ID (low) (assigned by the USB-IF) lsU8(descCommonVendorID), // Vendor ID (low) (assigned by the USB-IF)
msU8(descVendorID), // Vendor ID (high) (assigned by the USB-IF) msU8(descCommonVendorID), // Vendor ID (high) (assigned by the USB-IF)
lsU8(descProductID), // Product ID (low) (assigned by the manufacturer) lsU8(descCommonProductID), // Product ID (low) (assigned by the manufacturer)
msU8(descProductID), // Product ID (high) (assigned by the manufacturer) msU8(descCommonProductID), // Product ID (high) (assigned by the manufacturer)
lsU8(descReleaseID), // Device release number in BCD (low) lsU8(descCommonReleaseID), // Device release number in BCD (low)
msU8(descReleaseID), // Device release number in BCD (high) msU8(descCommonReleaseID), // Device release number in BCD (high)
1, // Index of string descriptor describing manufacturer 1, // Index of string descriptor describing manufacturer
2, // Index of string descriptor describing product 2, // Index of string descriptor describing product
3, // Index of string descriptor describing the device's serial number 3, // Index of string descriptor describing the device's serial number
descCDCACMCount, // Number of possible configurations descCDCACMCount, // Number of possible configurations
} }
// descCDCACM0Qualif holds the default device qualification descriptor for // descCDCACM0Qualif holds the default device qualification descriptor for
@@ -497,24 +463,24 @@ var descCDCACM0Config = [descCDCACMConfigSize]uint8{
// descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer. // descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer.
const descCDCACMCodingSize = 7 const descCDCACMCodingSize = 7
// descCDCACM0Coding holds the default UART line coding for CDC-ACM[0], i.e., // descCDCACM0LineCoding holds the default UART line coding for CDC-ACM[0],
// configuration index 1. // i.e., configuration index 1.
var descCDCACM0Coding [descCDCACMCodingSize]uint8 var descCDCACM0LineCoding descCDCACMLineCoding
type descCDCACMLineCoding struct { type descCDCACMLineCoding struct {
baud uint32 baud uint32
stopBits uint8 stopBits uint8
parity uint8 parity uint8
numBits uint8 numBits uint8
dtr bool rtsdtr uint8
rts bool
} }
func (lc *descCDCACMLineCoding) parse(buffer []uint8) bool { func (lc *descCDCACMLineCoding) parse(buffer []uint8) bool {
if len(buffer) < descCDCACMCodingSize { if len(buffer) < descCDCACMCodingSize {
return false return false
} }
lc.baud = packU32(buffer) _ = copy(buffer[:], buffer)
lc.baud = packU32(buffer[:])
lc.stopBits = buffer[4] lc.stopBits = buffer[4]
if 0 == lc.stopBits { if 0 == lc.stopBits {
lc.stopBits = 1 lc.stopBits = 1
@@ -523,3 +489,49 @@ func (lc *descCDCACMLineCoding) parse(buffer []uint8) bool {
lc.numBits = buffer[6] lc.numBits = buffer[6]
return true return true
} }
const (
descStringIndexCount = 4 // Language, Manufacturer, Product, Serial Number
descStringSize = 64 // (64-2)/2 = 31 chars each (UTF-16 code points)
// The maximum allowable string descriptor size is 255, or (255-2)/2 = 126
// available UTF-16 code points. Considering we are allocating this storage at
// compile-time, it seems like an awful waste of space (255*4 = ~1 KiB) just
// to store four strings, which, in all likelihood, will not be modified by
// anyone other than TinyGo devs; 64*4 = 256 B (i.e., 31 UTF-16 code points
// for each string) seems a good compromise.
)
type (
// descString is the actual byte array used to hold string descriptors. The
// first two bytes are a USB-specified header (0=length, 1=type), and the
// remaining bytes are UTF-16 code points, ordered low byte-first. If you just
// want to use UTF-8 (or even ASCII), you still need to reserve 2 bytes for
// each symbol, but you can set all of their high bytes 0.
descString [descStringSize]uint8
// descStringIndex defines an indexed collection of string descriptors for a
// given language.
descStringIndex [descStringIndexCount]descString
// descStringLanguage contains a language code and an indexed collection of
// string descriptors encoded in that language.
descStringLanguage struct {
language uint16
descriptor descStringIndex
}
)
// descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e.,
// configuration index 1.
var descCDCACM0String = [descCDCACMLanguageCount]descStringLanguage{
{ // US English string descriptors
language: descLanguageEnglish,
descriptor: descStringIndex{
{ // Language (index 0)
4,
descTypeString,
lsU8(descLanguageEnglish),
msU8(descLanguageEnglish),
},
// Actual string descriptors (index > 0) are copied into here at runtime!
},
},
}
+20 -15
View File
@@ -7,13 +7,14 @@ const descCPUFrequencyHz = 600000000
// General USB device identification constants. // General USB device identification constants.
const ( const (
descVendorID = 0x16C0 descCommonVendorID = 0x16C0
descProductID = 0x0483 descCommonProductID = 0x0483
descReleaseID = 0x0101 descCommonReleaseID = 0x0101 // BCD (1.1)
descManufacturer = "NXP Semiconductors" descCommonLanguage = descLanguageEnglish
descProduct = "TinyGo USB" descCommonManufacturer = "NXP Semiconductors"
descSerialNumber = "0000000000" descCommonProduct = "TinyGo USB"
descCommonSerialNumber = "1"
) )
// Constants for USB CDC-ACM device classes. // Constants for USB CDC-ACM device classes.
@@ -31,6 +32,9 @@ const (
descCDCACMMaxPower = 50 // 100 mA descCDCACMMaxPower = 50 // 100 mA
descCDCACMTxTimeoutMs = 120 // millisec
descCDCACMTxSyncUs = 75 // microsec
descCDCACMStatusPacketSize = 16 descCDCACMStatusPacketSize = 16
descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // high-speed descCDCACMDataRxPacketSize = descCDCACMDataRxHSPacketSize // high-speed
descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // high-speed descCDCACMDataTxPacketSize = descCDCACMDataTxHSPacketSize // high-speed
@@ -117,14 +121,13 @@ var descCDCACM0RDIdx [descCDCACMRDCount]uint16
var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16 var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16
type descCDCACMClass struct { type descCDCACMClass struct {
locstr *[descCDCACMLanguageCount]descLocalStrings // string descriptors locale *[descCDCACMLanguageCount]descStringLanguage // string descriptors
device *[descLengthDevice]uint8 // device descriptor device *[descLengthDevice]uint8 // device descriptor
qualif *[descLengthQualification]uint8 // device qualification descriptor qualif *[descLengthQualification]uint8 // device qualification descriptor
config *[descCDCACMConfigSize]uint8 // configuration descriptor config *[descCDCACMConfigSize]uint8 // configuration descriptor
coding *[descCDCACMCodingSize]uint8 // UART line coding lineCoding *descCDCACMLineCoding // UART line coding active state
cticks int64 // lineActive int64 // time since last UART DTR/RTS
rtsdtr uint8
qh *[descCDCACMQHCount]dcdEndpoint // endpoint queue heads qh *[descCDCACMQHCount]dcdEndpoint // endpoint queue heads
@@ -144,6 +147,7 @@ type descCDCACMClass struct {
txHead uint8 txHead uint8
txFree uint16 txFree uint16
txPrev bool
rxHead uint8 rxHead uint8
rxTail uint8 rxTail uint8
@@ -157,14 +161,15 @@ type descCDCACMClass struct {
// descCDCACM holds the configuration, endpoint, and transfer descriptors, along // descCDCACM holds the configuration, endpoint, and transfer descriptors, along
// with the buffers and control states, for all of the CDC-ACM (single) device // with the buffers and control states, for all of the CDC-ACM (single) device
// class configurations, ordered by configuration index (offset by -1). // class configurations, ordered by configuration index (offset by -1).
//go:align 32
var descCDCACM = [descCDCACMCount]descCDCACMClass{ var descCDCACM = [descCDCACMCount]descCDCACMClass{
{ {
locstr: &descCDCACM0String, locale: &descCDCACM0String,
device: &descCDCACM0Device, device: &descCDCACM0Device,
qualif: &descCDCACM0Qualif, qualif: &descCDCACM0Qualif,
config: &descCDCACM0Config, config: &descCDCACM0Config,
coding: &descCDCACM0Coding, lineCoding: &descCDCACM0LineCoding,
qh: &descCDCACM0QH, qh: &descCDCACM0QH,
+1
View File
@@ -1,6 +1,7 @@
package usb2 package usb2
type hcd interface { type hcd interface {
class() class
init() status init() status
enable(enable bool) status enable(enable bool) status
critical(enter bool) status critical(enter bool) status
+7 -4
View File
@@ -20,10 +20,10 @@ const hcdInterruptPriority = 3
// hostController implements USB host controller driver (hcd) interface. // hostController implements USB host controller driver (hcd) interface.
type hostController struct { type hostController struct {
core *core // Parent USB core this instance is attached to core *core // Parent USB core this instance is attached to
port int // USB port index port int // USB port index
class class // USB host class cc class // USB host class
id int // hostControllerInstance index id int // hostControllerInstance index
bus *nxp.USB_Type bus *nxp.USB_Type
phy *nxp.USBPHY_Type phy *nxp.USBPHY_Type
@@ -50,6 +50,7 @@ func initHCD(port int, class class) (hcd, status) {
// Initialize host controller. // Initialize host controller.
hostControllerInstance[i].core = &coreInstance[port] hostControllerInstance[i].core = &coreInstance[port]
hostControllerInstance[i].port = port hostControllerInstance[i].port = port
hostControllerInstance[i].cc = class
hostControllerInstance[i].id = i hostControllerInstance[i].id = i
switch port { switch port {
case 0: case 0:
@@ -76,6 +77,8 @@ func initHCD(port int, class class) (hcd, status) {
return nil, statusBusy // No free host controller instances available. return nil, statusBusy // No free host controller instances available.
} }
func (hc *hostController) class() class { return hc.cc }
func (hc *hostController) init() status { func (hc *hostController) init() status {
return statusOK return statusOK
+59 -3
View File
@@ -5,7 +5,10 @@ import (
) )
var ( var (
ErrInvalidPort = errors.New("invalid USB port") 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")
) )
type ( type (
@@ -24,14 +27,67 @@ type (
func (uart *UART) Configure(config UARTConfig) error { func (uart *UART) Configure(config UARTConfig) error {
if uart.port >= CoreCount || uart.port >= dcdCount { if uart.port >= CoreCount || uart.port >= dcdCount {
return ErrInvalidPort return ErrUARTInvalidPort
} }
// verify we have a free USB port and take ownership of it // verify we have a free USB port and take ownership of it
var st status var st status
uart.core, st = initCore(uart.port, class{id: classDeviceCDCACM, config: 1}) uart.core, st = initCore(uart.port, class{id: classDeviceCDCACM, config: 1})
if !st.ok() { if !st.ok() {
return ErrInvalidPort return ErrUARTInvalidPort
} }
return nil return nil
} }
// Buffered returns the number of bytes currently stored in the RX buffer.
func (uart UART) Buffered() int {
dc, ok := uart.core.dc.(*deviceController)
if !ok {
return 0
}
return 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) {
dc, ok := uart.core.dc.(*deviceController)
if !ok {
return 0, ErrUARTInvalidCore
}
n, ok := dc.uartReadByte()
if !ok {
return 0, ErrUARTEmptyBuffer
}
return n, nil
}
// Read from the RX buffer.
func (uart UART) Read(data []byte) (n int, err error) {
dc, ok := uart.core.dc.(*deviceController)
if !ok {
return 0, ErrUARTInvalidCore
}
return dc.uartRead(data), nil
}
// WriteByte writes a single byte of data to the UART interface.
func (uart UART) WriteByte(c byte) error {
dc, ok := uart.core.dc.(*deviceController)
if !ok {
return ErrUARTInvalidCore
}
if !dc.uartWriteByte(c) {
return ErrUARTWriteFailed
}
return nil
}
// Write data to the UART.
func (uart UART) Write(data []byte) (n int, err error) {
dc, ok := uart.core.dc.(*deviceController)
if !ok {
return 0, ErrUARTInvalidCore
}
return dc.uartWrite(data), nil
}
+25
View File
@@ -48,6 +48,13 @@ func (cl class) mode() int {
} }
} }
// equals returns true if and only if all fields of the given class are equal to
// those of the receiver cl.
//go:inline
func (cl class) equals(class class) bool {
return cl.id == class.id && cl.config == class.config
}
// CoreCount defines the total number of USB cores to configure in device or // CoreCount defines the total number of USB cores to configure in device or
// host mode. // host mode.
const CoreCount = dcdCount + hcdCount const CoreCount = dcdCount + hcdCount
@@ -79,7 +86,25 @@ func initCore(port int, class class) (*core, status) {
if port < 0 || port >= CoreCount || 0 == class.config { if port < 0 || port >= CoreCount || 0 == class.config {
return nil, statusInvalid return nil, statusInvalid
} }
if modeIdle != coreInstance[port].mode { if modeIdle != coreInstance[port].mode {
// Check if requested port is already configured as requested class. If so,
// just return a reference to the existing core instead of an error.
// For instance, this will allow TinyGo examples that try to reconfigure the
// USB (CDC-ACM) UART port (which is already configured by the runtime) to
// continue without error.
if coreInstance[port].mode == class.mode() {
switch class.mode() {
case modeDevice:
if coreInstance[port].dc.class().equals(class) {
return &coreInstance[port], statusOK
}
case modeHost:
if coreInstance[port].hc.class().equals(class) {
return &coreInstance[port], statusOK
}
}
}
return nil, statusBusy return nil, statusBusy
} }
+1 -1
View File
@@ -124,7 +124,7 @@ func initUART() {
} }
func initUSB() { func initUSB() {
machine.USBCDC0.Configure(machine.UARTConfig{}) machine.UART0.Configure(machine.UARTConfig{})
} }
func putchar(c byte) { func putchar(c byte) {