mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-18 03:24:00 +00:00
USB CDC-ACM UART Rx/Tx functioning for baseline target (Teensy 4.0/4.1)
This commit is contained in:
@@ -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(),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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!
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user