mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-09 05:23:40 +00:00
begin USB refactor with package machine/usb2
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
package usb2
|
||||
|
||||
type dci interface {
|
||||
init() status
|
||||
enable(enable bool) status
|
||||
critical(enter bool) status
|
||||
interrupt()
|
||||
udelay(micros uint32)
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb2
|
||||
|
||||
// Implementation of USB device controller interface (dci) for NXP iMXRT1062.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nxp"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// dciCount defines the number of USB cores to configure for device mode. It is
|
||||
// computed as the sum of all declared device configuration descriptors.
|
||||
const dciCount = descCDCACMConfigCount
|
||||
|
||||
// dciInterruptPriority defines the priority for all USB device interrupts.
|
||||
const dciInterruptPriority = 3
|
||||
|
||||
// deviceController implements USB device controller interface (dci).
|
||||
type deviceController struct {
|
||||
core *core // Parent USB core this instance is attached to
|
||||
port int // USB port index
|
||||
id int // deviceControllerInstance index
|
||||
|
||||
bus *nxp.USB_Type
|
||||
phy *nxp.USBPHY_Type
|
||||
irq interrupt.Interrupt
|
||||
|
||||
cri volatile.Register8 // set to 1 if in critical section, else 0
|
||||
ivm uintptr // interrupt state when entering critical section
|
||||
}
|
||||
|
||||
// deviceControllerInstance provides statically-allocated instances of each USB
|
||||
// device controller configured on this platform.
|
||||
var deviceControllerInstance [dciCount]deviceController
|
||||
|
||||
// initDCI initializes and assigns a free device controller instance to the
|
||||
// given USB port. Returns the initialized device controller or nil if no free
|
||||
// device controller instances remain.
|
||||
func initDCI(port int) (dci, status) {
|
||||
if 0 == dciCount {
|
||||
return nil, statusInvalidArgument // must have defined device descriptors
|
||||
}
|
||||
// Return the first instance whose assigned core is currently nil.
|
||||
for i := range deviceControllerInstance {
|
||||
if nil == deviceControllerInstance[i].core {
|
||||
// Initialize device controller.
|
||||
deviceControllerInstance[i].core = &coreInstance[port]
|
||||
deviceControllerInstance[i].port = port
|
||||
deviceControllerInstance[i].id = i
|
||||
switch port {
|
||||
case 0:
|
||||
deviceControllerInstance[i].bus = nxp.USB1
|
||||
deviceControllerInstance[i].phy = nxp.USBPHY1
|
||||
deviceControllerInstance[i].irq =
|
||||
interrupt.New(nxp.IRQ_USB_OTG1,
|
||||
func(interrupt.Interrupt) {
|
||||
coreInstance[0].dc.interrupt()
|
||||
})
|
||||
|
||||
case 1:
|
||||
deviceControllerInstance[i].bus = nxp.USB2
|
||||
deviceControllerInstance[i].phy = nxp.USBPHY2
|
||||
deviceControllerInstance[i].irq =
|
||||
interrupt.New(nxp.IRQ_USB_OTG2,
|
||||
func(interrupt.Interrupt) {
|
||||
//coreInstance[1].dc.interrupt()
|
||||
})
|
||||
}
|
||||
return &deviceControllerInstance[i], statusOK
|
||||
}
|
||||
}
|
||||
return nil, statusBusy // No free device controller instances available.
|
||||
}
|
||||
|
||||
func (dc *deviceController) init() status {
|
||||
|
||||
dc.bus.BURSTSIZE.Set(0x0404)
|
||||
|
||||
// if dc.phy.PWD.HasBits((nxp.USBPHY_PWD_RXPWDRX | nxp.USBPHY_PWD_RXPWDDIFF |
|
||||
// nxp.USBPHY_PWD_RXPWD1PT1 | nxp.USBPHY_PWD_RXPWDENV |
|
||||
// nxp.USBPHY_PWD_TXPWDV2I | nxp.USBPHY_PWD_TXPWDIBIAS |
|
||||
// nxp.USBPHY_PWD_TXPWDFS)) ||
|
||||
// dc.bus.USBMODE.HasBits(nxp.USB_USBMODE_CM_Msk) {
|
||||
// // reset controller if it was already enabled
|
||||
// dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST)
|
||||
// dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST)
|
||||
// for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) {
|
||||
// }
|
||||
// // clear interrupts
|
||||
// m := arm.DisableInterrupts()
|
||||
// switch dc.port {
|
||||
// case 0:
|
||||
// arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1))
|
||||
// case 1:
|
||||
// arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2))
|
||||
// }
|
||||
// dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_SFTRST)
|
||||
// }
|
||||
|
||||
// reset the controller
|
||||
dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST)
|
||||
dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST)
|
||||
for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) {
|
||||
}
|
||||
// clear interrupts
|
||||
m := arm.DisableInterrupts()
|
||||
switch dc.port {
|
||||
case 0:
|
||||
arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1))
|
||||
case 1:
|
||||
arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2))
|
||||
}
|
||||
dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_CLKGATE | nxp.USBPHY_CTRL_SFTRST)
|
||||
dc.phy.PWD.Set(0)
|
||||
|
||||
// clear the controller mode field and set to device mode:
|
||||
// controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only
|
||||
dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2,
|
||||
nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos)
|
||||
|
||||
dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) // no interrupt threshold
|
||||
dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) // disable setup lockout
|
||||
dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) // use little-endianness
|
||||
|
||||
// configure ENDPOINTLISTADDR
|
||||
|
||||
// enable interrupts
|
||||
dc.bus.USBINTR.Set(
|
||||
nxp.USB_USBINTR_UE_Msk | // bus enable
|
||||
nxp.USB_USBINTR_UEE_Msk | // bus error
|
||||
nxp.USB_USBINTR_PCE_Msk | // port change detect
|
||||
nxp.USB_USBINTR_URE_Msk | // bus reset
|
||||
nxp.USB_USBINTR_SLE) // sleep enable
|
||||
|
||||
// ensure D+ pulled down long enough for host to detect previous disconnect
|
||||
dc.udelay(5000)
|
||||
|
||||
return statusOK
|
||||
}
|
||||
|
||||
func (dc *deviceController) enable(enable bool) status {
|
||||
|
||||
dc.irq.SetPriority(dciInterruptPriority)
|
||||
dc.irq.Enable()
|
||||
|
||||
dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS)
|
||||
|
||||
return statusOK
|
||||
}
|
||||
|
||||
func (dc *deviceController) critical(enter bool) status {
|
||||
if enter {
|
||||
// check if critical section already locked
|
||||
if dc.cri.Get() != 0 {
|
||||
return statusRetry
|
||||
}
|
||||
// lock critical section
|
||||
dc.cri.Set(1)
|
||||
// disable interrupts, storing state in receiver
|
||||
dc.ivm = arm.DisableInterrupts()
|
||||
} else {
|
||||
// ensure critical section is locked
|
||||
if dc.cri.Get() != 0 {
|
||||
// re-enable interrupts, using state stored in receiver
|
||||
arm.EnableInterrupts(dc.ivm)
|
||||
// unlock critical section
|
||||
dc.cri.Set(0)
|
||||
}
|
||||
}
|
||||
return statusOK
|
||||
}
|
||||
|
||||
func (dc *deviceController) interrupt() {
|
||||
// read and clear the interrupts that fired
|
||||
status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get()
|
||||
dc.bus.USBSTS.Set(status)
|
||||
|
||||
println(strconv.FormatUint(uint64(status), 16))
|
||||
}
|
||||
|
||||
// udelay waits for the given number of microseconds before returning.
|
||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
||||
// an approximate method to spin CPU cycles for short periods of time.
|
||||
//go:inline
|
||||
func (dc *deviceController) udelay(microsec uint32) {
|
||||
n := cycles(microsec, descCPUFrequencyHz)
|
||||
for i := uint32(0); i < n; i++ {
|
||||
arm.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
package usb2
|
||||
|
||||
const descUSBSpecVersion = 0x0200 // USB 2.0
|
||||
|
||||
// USB constants defined per specification.
|
||||
const (
|
||||
|
||||
// Descriptor length
|
||||
descLengthDevice = 18
|
||||
descLengthConfigure = 9
|
||||
descLengthInterface = 9
|
||||
descLengthEndpoint = 7
|
||||
descLengthDeviceQualitier = 10
|
||||
descLengthOTG = 5
|
||||
descLengthBOS = 5
|
||||
descLengthEndpointCompanion = 6
|
||||
descLengthDevCapTypeUSB20Extension = 7
|
||||
descLengthDevCapTypeSuperspeed = 10
|
||||
|
||||
// Descriptor type
|
||||
descTypeDevice = 0x01
|
||||
descTypeConfigure = 0x02
|
||||
descTypeString = 0x03
|
||||
descTypeInterface = 0x04
|
||||
descTypeEndpoint = 0x05
|
||||
descTypeDeviceQualitier = 0x06
|
||||
descTypeOtherSpeedConfiguration = 0x07
|
||||
descTypeInterfacePower = 0x08
|
||||
descTypeOTG = 0x09
|
||||
descTypeInterfaceAssociation = 0x0B
|
||||
descTypeBOS = 0x0F
|
||||
descTypeDeviceCapability = 0x10
|
||||
descTypeHID = 0x21
|
||||
descTypeHIDReport = 0x22
|
||||
descTypeHIDPhysical = 0x23
|
||||
descTypeCDCInterface = 0x24
|
||||
descTypeCDCEndpoint = 0x25
|
||||
descTypeEndpointCompanion = 0x30
|
||||
|
||||
// Configuration attributes
|
||||
descConfigAttrD7Msk = 0x80
|
||||
descConfigAttrD7Pos = 7
|
||||
descConfigAttrSelfPoweredMsk = 0x40
|
||||
descConfigAttrSelfPoweredPos = 6
|
||||
descConfigAttrRemoteWakeupMsk = 0x20
|
||||
descConfigAttrRemoteWakeupPos = 5
|
||||
|
||||
// Endpoint type
|
||||
descEndptTypeControl = 0x00
|
||||
descEndptTypeIsochronous = 0x01
|
||||
descEndptTypeBulk = 0x02
|
||||
descEndptTypeInterrupt = 0x03
|
||||
|
||||
// Endpoint address
|
||||
descEndptAddrNumberMsk = 0x0F
|
||||
descEndptAddrNumberPos = 0
|
||||
descEndptAddrDirectionMsk = 0x80
|
||||
descEndptAddrDirectionPos = 7
|
||||
descEndptAddrDirectionOut = 0
|
||||
descEndptAddrDirectionIn = 0x80
|
||||
|
||||
// Endpoint attributes
|
||||
descEndptAttrTypeMsk = 0x03
|
||||
descEndptAttrNumberPos = 0
|
||||
descEndptAttrSyncTypeMsk = 0x0C
|
||||
descEndptAttrSyncTypePos = 2
|
||||
descEndptAttrSyncTypeNoSync = 0x00
|
||||
descEndptAttrSyncTypeAsync = 0x04
|
||||
descEndptAttrSyncTypeAdaptive = 0x08
|
||||
descEndptAttrSyncTypeSync = 0x0C
|
||||
descEndptAttrUsageTypeMsk = 0x30
|
||||
descEndptAttrUsageTypePos = 4
|
||||
descEndptAttrUsageTypeDataEndpoint = 0x00
|
||||
descEndptAttrUsageTypeFeedEndpoint = 0x10
|
||||
descEndptAttrUsageTypeFeedDataEndpoint = 0x20
|
||||
|
||||
// Endpoint max packet size
|
||||
descEndptMaxPktSizeSizeMsk = 0x07FF
|
||||
descEndptMaxPktSizeMultTransMsk = 0x1800
|
||||
descEndptMaxPktSizeMultTransPos = 11
|
||||
descEndptMaxPktSizeMaximum = 64
|
||||
|
||||
// OTG attributes
|
||||
descOTGAttrSRPMsk = 0x01
|
||||
descOTGAttrHNPMsk = 0x02
|
||||
descOTGAttrADPMsk = 0x04
|
||||
|
||||
// Device capability type
|
||||
descDevCapTypeWireless = 0x01
|
||||
descDevCapTypeUSB20Extension = 0x02
|
||||
descDevCapTypeSuperspeed = 0x03
|
||||
|
||||
// Device capability attributes (USB 2.0 extension)
|
||||
descDevCapExtAttrLPMMsk = 0x02
|
||||
descDevCapExtAttrLPMPos = 1
|
||||
descDevCapExtAttrBESLMsk = 0x04
|
||||
descDevCapExtAttrBESLPos = 2
|
||||
)
|
||||
|
||||
// USB CDC constants defined per specification.
|
||||
const (
|
||||
|
||||
// Device class
|
||||
descCDCComm = 0x02 // communication/control
|
||||
descCDCData = 0x0A // data
|
||||
|
||||
// Communication/control subclass
|
||||
descCDCSubNone = 0x00
|
||||
descCDCSubDirectLineControl = 0x01
|
||||
descCDCSubAbstractControl = 0x02
|
||||
descCDCSubTelephoneControl = 0x03
|
||||
descCDCSubMultiChannelControl = 0x04
|
||||
descCDCSubCAPIControl = 0x05
|
||||
descCDCSubEthernetNetworkingControl = 0x06
|
||||
descCDCSubATMNetworkingControl = 0x07
|
||||
descCDCSubWirelessHandsetControl = 0x08
|
||||
descCDCSubDeviceManagement = 0x09
|
||||
descCDCSubMobileDirectLine = 0x0A
|
||||
descCDCSubOBEX = 0x0B
|
||||
descCDCSubEthernetEmulation = 0x0C
|
||||
|
||||
// Communication/control protocol
|
||||
descCDCProtoNone = 0x00 // also for data class
|
||||
descCDCProtoAT250 = 0x01
|
||||
descCDCProtoATPCCA101 = 0x02
|
||||
descCDCProtoATPCCA101AnnexO = 0x03
|
||||
descCDCProtoATGSM707 = 0x04
|
||||
descCDCProtoAT3GPP27007 = 0x05
|
||||
descCDCProtoATTIACDMA = 0x06
|
||||
descCDCProtoEthernetEmulation = 0x07
|
||||
descCDCProtoExternal = 0xFE
|
||||
descCDCProtoVendorSpecific = 0xFF // also for data class
|
||||
|
||||
// Data protocol
|
||||
descCDCProtoPyhsicalInterface = 0x30
|
||||
descCDCProtoHDLC = 0x31
|
||||
descCDCProtoTransparent = 0x32
|
||||
descCDCProtoManagement = 0x50
|
||||
descCDCProtoDataLinkQ931 = 0x51
|
||||
descCDCProtoDataLinkQ921 = 0x52
|
||||
descCDCProtoDataCompressionV42BIS = 0x90
|
||||
descCDCProtoEuroISDN = 0x91
|
||||
descCDCProtoRateAdaptionISDNV24 = 0x92
|
||||
descCDCProtoCAPICommands = 0x93
|
||||
descCDCProtoHostBasedDriver = 0xFD
|
||||
descCDCProtoUnitFunctional = 0xFE
|
||||
|
||||
// Functional descriptor length
|
||||
descCDCLengthFuncHeader = 5
|
||||
descCDCLengthFuncCallManagement = 5
|
||||
descCDCLengthFuncAbstractControl = 4
|
||||
descCDCLengthFuncUnion = 5
|
||||
|
||||
// Functional descriptor type
|
||||
descCDCTypeFuncHeader = 0x00
|
||||
descCDCTypeFuncCallManagement = 0x01
|
||||
descCDCTypeFuncAbstractControl = 0x02
|
||||
descCDCTypeFuncDirectLine = 0x03
|
||||
descCDCTypeFuncTelephoneRinger = 0x04
|
||||
descCDCTypeFuncTelephoneReport = 0x05
|
||||
descCDCTypeFuncUnion = 0x06
|
||||
descCDCTypeFuncCountrySelect = 0x07
|
||||
descCDCTypeFuncTelephoneModes = 0x08
|
||||
descCDCTypeFuncTerminal = 0x09
|
||||
descCDCTypeFuncNetworkChannel = 0x0A
|
||||
descCDCTypeFuncProtocolUnit = 0x0B
|
||||
descCDCTypeFuncExtensionUnit = 0x0C
|
||||
descCDCTypeFuncMultiChannel = 0x0D
|
||||
descCDCTypeFuncCAPIControl = 0x0E
|
||||
descCDCTypeFuncEthernetNetworking = 0x0F
|
||||
descCDCTypeFuncATMNetworking = 0x10
|
||||
descCDCTypeFuncWirelessControl = 0x11
|
||||
descCDCTypeFuncMobileDirectLine = 0x12
|
||||
descCDCTypeFuncMDLMDetail = 0x13
|
||||
descCDCTypeFuncDeviceManagement = 0x14
|
||||
descCDCTypeFuncOBEX = 0x15
|
||||
descCDCTypeFuncCommandSet = 0x16
|
||||
descCDCTypeFuncCommandSetDetail = 0x17
|
||||
descCDCTypeFuncTelephoneControl = 0x18
|
||||
descCDCTypeFuncOBEXServiceID = 0x19
|
||||
)
|
||||
|
||||
// Common configuration constants for the USB CDC-ACM (single) device class.
|
||||
const (
|
||||
// Interfaces for the first CDC-ACM device (index 1).
|
||||
descCDCACM0InterfaceCount = 2
|
||||
descCDCACM0InterfaceCtrl = 0
|
||||
descCDCACM0InterfaceData = 1
|
||||
// Endpoints for the first CDC-ACM device (index 1).
|
||||
descCDCACM0EndpointCount = 4
|
||||
descCDCACM0EndpointStatus = 2 // Communication/control interrupt input
|
||||
descCDCACM0EndpointDataRx = 3 // Bulk data output
|
||||
descCDCACM0EndpointDataTx = 4 // Bulk data input
|
||||
|
||||
// Size of all CDC-ACM configuration descriptors.
|
||||
descCDCACMConfigSize = uint16(
|
||||
descLengthConfigure + // configuration
|
||||
descLengthInterface + // communication/control interface
|
||||
descCDCLengthFuncHeader + // CDC header
|
||||
descCDCLengthFuncCallManagement + // CDC call management
|
||||
descCDCLengthFuncAbstractControl + // CDC abstract control
|
||||
descCDCLengthFuncUnion + // CDC union
|
||||
descLengthEndpoint + // communication/control input endpoint
|
||||
descLengthInterface + // data interface
|
||||
descLengthEndpoint + // data input endpoint
|
||||
descLengthEndpoint) // data output endpoint
|
||||
// Attributes of all CDC-ACM configuration descriptors.
|
||||
descCDCACMConfigAttr = descConfigAttrD7Msk | // Bit 7: reserved (1)
|
||||
(1 << descConfigAttrSelfPoweredPos) | // Bit 6: self-powered
|
||||
(0 << descConfigAttrRemoteWakeupPos) | // Bit 5: remote wakeup
|
||||
0 // Bits 0-4: reserved (0)
|
||||
)
|
||||
|
||||
// Common descriptors for the USB CDC-ACM (single) device class.
|
||||
var (
|
||||
// descDeviceCDCACM holds the default device descriptors for CDC-ACM devices.
|
||||
descDeviceCDCACM = [descCDCACMConfigCount][descLengthDevice]uint8{
|
||||
{
|
||||
descLengthDevice, // Size of this descriptor in bytes
|
||||
descTypeDevice, // DEVICE Descriptor Type
|
||||
lsU8(descUSBSpecVersion), // USB Specification Release Number in BCD (low)
|
||||
msU8(descUSBSpecVersion), // USB Specification Release Number in BCD (high)
|
||||
descCDCComm, // Class code (assigned by the USB-IF).
|
||||
descCDCSubNone, // Subclass code (assigned by the USB-IF).
|
||||
descCDCProtoNone, // Protocol code (assigned by the USB-IF).
|
||||
descEndptMaxPktSizeMaximum, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
|
||||
lsU8(descVendorID), // Vendor ID (low) (assigned by the USB-IF)
|
||||
msU8(descVendorID), // Vendor ID (high) (assigned by the USB-IF)
|
||||
lsU8(descProductID), // Product ID (low) (assigned by the manufacturer)
|
||||
msU8(descProductID), // Product ID (high) (assigned by the manufacturer)
|
||||
lsU8(descReleaseID), // Device release number in BCD (low)
|
||||
msU8(descReleaseID), // Device release number in BCD (high)
|
||||
1, // Index of string descriptor describing manufacturer
|
||||
2, // Index of string descriptor describing product
|
||||
0, // Index of string descriptor describing the device's serial number
|
||||
descCDCACMConfigCount, // Number of possible configurations
|
||||
},
|
||||
}
|
||||
|
||||
// descConfigCDCACM holds the default configuration descriptors for CDC-ACM
|
||||
// devices.
|
||||
descConfigCDCACM = [descCDCACMConfigCount][descCDCACMConfigSize]uint8{
|
||||
{
|
||||
descLengthConfigure, // Size of this descriptor in bytes
|
||||
descTypeConfigure, // Descriptor Type
|
||||
lsU8(descCDCACMConfigSize), // Total length of data returned for this configuration (low)
|
||||
msU8(descCDCACMConfigSize), // Total length of data returned for this configuration (high)
|
||||
descCDCACM0InterfaceCount, // Number of interfaces supported by this configuration
|
||||
1, // Value to use to select this configuration (1 = CDC-ACM[0])
|
||||
0, // Index of string descriptor describing this configuration
|
||||
descCDCACMConfigAttr, // Configuration attributes
|
||||
descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units)
|
||||
|
||||
// Communication/Control Interface Descriptor
|
||||
descLengthInterface, // Descriptor length
|
||||
descTypeInterface, // Descriptor type
|
||||
descCDCACM0InterfaceCtrl, // Interface index
|
||||
0, // Alternate setting
|
||||
1, // Number of endpoints
|
||||
descCDCComm, // Class code
|
||||
descCDCSubAbstractControl, // Subclass code
|
||||
descCDCProtoNone, // Protocol code (NOTE: Teensyduino defines this as 1 [AT V.250])
|
||||
0, // Interface Description String Index
|
||||
|
||||
// CDC Header Functional Descriptor
|
||||
descCDCLengthFuncHeader, // Size of this descriptor in bytes
|
||||
descTypeCDCInterface, // Descriptor Type
|
||||
descCDCTypeFuncHeader, // Descriptor Subtype
|
||||
0x10, // USB CDC specification version 1.10 (low)
|
||||
0x01, // USB CDC specification version 1.10 (high)
|
||||
|
||||
// CDC Call Management Functional Descriptor
|
||||
descCDCLengthFuncCallManagement, // Size of this descriptor in bytes
|
||||
descTypeCDCInterface, // Descriptor Type
|
||||
descCDCTypeFuncCallManagement, // Descriptor Subtype
|
||||
0x01, // Capabilities
|
||||
descCDCACM0InterfaceData, // Data Interface
|
||||
|
||||
// CDC Abstract Control Management Functional Descriptor
|
||||
descCDCLengthFuncAbstractControl, // Size of this descriptor in bytes
|
||||
descTypeCDCInterface, // Descriptor Type
|
||||
descCDCTypeFuncAbstractControl, // Descriptor Subtype
|
||||
0x06, // Capabilities
|
||||
|
||||
// CDC Union Functional Descriptor
|
||||
descCDCLengthFuncUnion, // Size of this descriptor in bytes
|
||||
descTypeCDCInterface, // Descriptor Type
|
||||
descCDCTypeFuncUnion, // Descriptor Subtype
|
||||
descCDCACM0InterfaceCtrl, // Controlling interface index
|
||||
descCDCACM0InterfaceData, // Controlled interface index
|
||||
|
||||
// Communication/Control Notification Endpoint descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descCDCACM0EndpointStatus | // Endpoint address
|
||||
descEndptAddrDirectionIn,
|
||||
descEndptTypeInterrupt, // Attributes
|
||||
lsU8(descCDCACMStatusPacketSize), // Max packet size (low)
|
||||
msU8(descCDCACMStatusPacketSize), // Max packet size (high)
|
||||
8, // Polling Interval
|
||||
|
||||
// Data Interface Descriptor
|
||||
descLengthInterface, // Interface length
|
||||
descTypeInterface, // Interface type
|
||||
descCDCACM0InterfaceData, // Interface index
|
||||
0, // Alternate setting
|
||||
2, // Number of endpoints
|
||||
descCDCData, // Class code
|
||||
descCDCSubNone, // Subclass code
|
||||
descCDCProtoNone, // Protocol code
|
||||
0, // Interface Description String Index
|
||||
|
||||
// Data Bulk Rx Endpoint descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descCDCACM0EndpointDataRx | // Endpoint address
|
||||
descEndptAddrDirectionOut,
|
||||
descEndptTypeBulk, // Attributes
|
||||
lsU8(descCDCACMDataRxPacketSize), // Max packet size (low)
|
||||
msU8(descCDCACMDataRxPacketSize), // Max packet size (high)
|
||||
0, // Polling Interval
|
||||
|
||||
// Data Bulk Tx Endpoint descriptor
|
||||
descLengthEndpoint, // Size of this descriptor in bytes
|
||||
descTypeEndpoint, // Descriptor Type
|
||||
descCDCACM0EndpointDataTx | // Endpoint address
|
||||
descEndptAddrDirectionIn,
|
||||
descEndptTypeBulk, // Attributes
|
||||
lsU8(descCDCACMDataTxPacketSize), // Max packet size (low)
|
||||
msU8(descCDCACMDataTxPacketSize), // Max packet size (high)
|
||||
0, // Polling Interval
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
package usb2
|
||||
|
||||
// descCPUFrequencyHz defines the target CPU frequency (Hz).
|
||||
const descCPUFrequencyHz = 600000000
|
||||
|
||||
// General USB device identification constants.
|
||||
const (
|
||||
descVendorID = 0xABCD
|
||||
descProductID = 0x1234
|
||||
descReleaseID = 0x0101
|
||||
|
||||
descVendor = "NXP Semiconductors"
|
||||
descProduct = "TinyGo USB"
|
||||
)
|
||||
|
||||
// Constants for USB CDC-ACM device classes.
|
||||
const (
|
||||
// descCDCACMConfigCount defines the number of USB cores that will be
|
||||
// configured as CDC-ACM (single) devices.
|
||||
descCDCACMConfigCount = 1
|
||||
|
||||
descCDCACMMaxPower = 50 // 100 mA
|
||||
|
||||
descCDCACMStatusPacketSize = 16
|
||||
descCDCACMDataRxPacketSize = descCDCACMDataRxFSPacketSize // full-speed
|
||||
descCDCACMDataTxPacketSize = descCDCACMDataTxFSPacketSize // full-speed
|
||||
|
||||
descCDCACMDataRxFSPacketSize = 64 // full-speed
|
||||
descCDCACMDataTxFSPacketSize = 64 // full-speed
|
||||
descCDCACMDataRxHSPacketSize = 512 // high-speed
|
||||
descCDCACMDataTxHSPacketSize = 512 // high-speed
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package usb2
|
||||
|
||||
type hci interface {
|
||||
init() status
|
||||
enable(enable bool) status
|
||||
critical(enter bool) status
|
||||
interrupt()
|
||||
udelay(micros uint32)
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb2
|
||||
|
||||
// Implementation of USB host controller interface (hci) for NXP iMXRT1062.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nxp"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
// hciCount defines the number of USB cores to configure for host mode. It is
|
||||
// computed as the sum of all declared host configuration descriptors.
|
||||
const hciCount = 0
|
||||
|
||||
// hciInterruptPriority defines the priority for all USB host interrupts.
|
||||
const hciInterruptPriority = 3
|
||||
|
||||
// hostController implements USB host controller interface (hci).
|
||||
type hostController struct {
|
||||
core *core // Parent USB core this instance is attached to
|
||||
port int // USB port index
|
||||
id int // hostControllerInstance index
|
||||
|
||||
bus *nxp.USB_Type
|
||||
phy *nxp.USBPHY_Type
|
||||
irq interrupt.Interrupt
|
||||
|
||||
cri volatile.Register8 // set to 1 if in critical section, else 0
|
||||
ivm uintptr // interrupt state when entering critical section
|
||||
}
|
||||
|
||||
// hostControllerInstance provides statically-allocated instances of each USB
|
||||
// host controller configured on this platform.
|
||||
var hostControllerInstance [hciCount]hostController
|
||||
|
||||
// initHCI initializes and assigns a free host controller instance to the given
|
||||
// USB port. Returns the initialized host controller or nil if no free host
|
||||
// controller instances remain.
|
||||
func initHCI(port int) (hci, status) {
|
||||
if 0 == hciCount {
|
||||
return nil, statusInvalidArgument // must have defined host descriptors
|
||||
}
|
||||
// Return the first instance whose assigned core is currently nil.
|
||||
for i := range hostControllerInstance {
|
||||
if nil == hostControllerInstance[i].core {
|
||||
// Initialize host controller.
|
||||
hostControllerInstance[i].core = &coreInstance[port]
|
||||
hostControllerInstance[i].port = port
|
||||
hostControllerInstance[i].id = i
|
||||
switch port {
|
||||
case 0:
|
||||
hostControllerInstance[i].bus = nxp.USB1
|
||||
hostControllerInstance[i].phy = nxp.USBPHY1
|
||||
hostControllerInstance[i].irq =
|
||||
interrupt.New(nxp.IRQ_USB_OTG1,
|
||||
func(interrupt.Interrupt) {
|
||||
coreInstance[0].hc.interrupt()
|
||||
})
|
||||
|
||||
case 1:
|
||||
hostControllerInstance[i].bus = nxp.USB2
|
||||
hostControllerInstance[i].phy = nxp.USBPHY2
|
||||
hostControllerInstance[i].irq =
|
||||
interrupt.New(nxp.IRQ_USB_OTG2,
|
||||
func(interrupt.Interrupt) {
|
||||
//coreInstance[1].hc.interrupt()
|
||||
})
|
||||
}
|
||||
return &hostControllerInstance[i], statusOK
|
||||
}
|
||||
}
|
||||
return nil, statusBusy // No free host controller instances available.
|
||||
}
|
||||
|
||||
func (hc *hostController) init() status {
|
||||
|
||||
return statusOK
|
||||
}
|
||||
|
||||
func (hc *hostController) enable(enable bool) status {
|
||||
|
||||
hc.irq.SetPriority(hciInterruptPriority)
|
||||
hc.irq.Enable()
|
||||
|
||||
return statusOK
|
||||
}
|
||||
|
||||
func (hc *hostController) critical(enter bool) status {
|
||||
if enter {
|
||||
// check if critical section already locked
|
||||
if hc.cri.Get() != 0 {
|
||||
return statusRetry
|
||||
}
|
||||
// lock critical section
|
||||
hc.cri.Set(1)
|
||||
// disable interrupts, storing state in receiver
|
||||
hc.ivm = arm.DisableInterrupts()
|
||||
} else {
|
||||
// ensure critical section is locked
|
||||
if hc.cri.Get() != 0 {
|
||||
// re-enable interrupts, using state stored in receiver
|
||||
arm.EnableInterrupts(hc.ivm)
|
||||
// unlock critical section
|
||||
hc.cri.Set(0)
|
||||
}
|
||||
}
|
||||
return statusOK
|
||||
}
|
||||
|
||||
func (hc *hostController) interrupt() {
|
||||
|
||||
}
|
||||
|
||||
// udelay waits for the given number of microseconds before returning.
|
||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
||||
// an approximate method to spin CPU cycles for short periods of time.
|
||||
//go:inline
|
||||
func (hc *hostController) udelay(microsec uint32) {
|
||||
n := cycles(microsec, descCPUFrequencyHz)
|
||||
for i := uint32(0); i < n; i++ {
|
||||
arm.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package usb2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidPort = errors.New("invalid USB port")
|
||||
)
|
||||
|
||||
type (
|
||||
UARTConfig struct {
|
||||
BaudRate uint32
|
||||
}
|
||||
|
||||
// UART represents a virtual serial (UART) device emulation using the USB
|
||||
// CDC-ACM device class driver.
|
||||
UART struct {
|
||||
port int // USB port (core index, e.g., 0-1)
|
||||
core *core
|
||||
}
|
||||
)
|
||||
|
||||
func (uart *UART) Configure(config UARTConfig) error {
|
||||
|
||||
if uart.port >= CoreCount || uart.port >= dciCount ||
|
||||
uart.port >= descCDCACMConfigCount {
|
||||
return ErrInvalidPort
|
||||
}
|
||||
|
||||
// use default configuration index (1-based index; 0=invalid)
|
||||
// uart.config = configDeviceCDCACMConfigurationIndex
|
||||
|
||||
// modify the global basic configuration struct configDeviceCDCACM for our USB
|
||||
// port and configuration index.
|
||||
//
|
||||
// these settings are copied into the real CDC-ACM object, using interface
|
||||
// deviceClassDriver, via initialization method (*deviceCDCACM).init().
|
||||
|
||||
// change baud rate from default, if provided
|
||||
//if config.BaudRate != 0 {
|
||||
// configDeviceCDCACM[uart.port][uart.config-1].lineCodingBaudRate =
|
||||
// config.BaudRate
|
||||
//}
|
||||
|
||||
// verify we have a free USB port and take ownership of it
|
||||
var st status
|
||||
uart.core, st = initCore(uart.port, modeDevice)
|
||||
if !st.ok() {
|
||||
return ErrInvalidPort
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package usb2
|
||||
|
||||
// Hardware abstraction for USB ports configured as either host or device.
|
||||
|
||||
// core represents the core of a USB port configured as either host or device.
|
||||
type core struct {
|
||||
port int
|
||||
mode int
|
||||
dc dci
|
||||
hc hci
|
||||
}
|
||||
|
||||
// Constant definitions for USB core operating modes.
|
||||
const (
|
||||
modeIdle = 0
|
||||
modeDevice = 1
|
||||
modeHost = 2
|
||||
)
|
||||
|
||||
// CoreCount defines the total number of USB cores to configure in device or
|
||||
// host mode.
|
||||
const CoreCount = dciCount + hciCount
|
||||
|
||||
// coreInstance provides statically-allocated instances of each USB core
|
||||
// configured on this platform.
|
||||
var coreInstance [CoreCount]core
|
||||
|
||||
// status represents the return code of a subroutine.
|
||||
type status uint8
|
||||
|
||||
// Constant definitions for all status codes used within the package.
|
||||
const (
|
||||
statusOK status = iota // Success
|
||||
statusBusy // Busy
|
||||
statusRetry // Retry
|
||||
statusInvalidArgument // Invalid argument
|
||||
)
|
||||
|
||||
func (st status) ok() bool { return statusOK == st }
|
||||
|
||||
// initCore initializes a free USB core with given operating mode on the USB
|
||||
// port at given index, if available. Returns a reference to the initialized
|
||||
// core or nil if the core is unavailable.
|
||||
func initCore(port, mode int) (*core, status) {
|
||||
|
||||
if port < 0 || port >= CoreCount {
|
||||
return nil, statusInvalidArgument
|
||||
}
|
||||
if modeIdle != coreInstance[port].mode {
|
||||
return nil, statusBusy
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case modeDevice:
|
||||
// Allocate a free device controller and install interrupts
|
||||
dc, st := initDCI(port)
|
||||
if !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
// Initialize buffers and device descriptors
|
||||
if st = dc.init(); !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
coreInstance[port].port = port
|
||||
coreInstance[port].mode = mode
|
||||
coreInstance[port].dc = dc
|
||||
// Enable interrupts and enter runtime
|
||||
if st = dc.enable(true); !st.ok() {
|
||||
coreInstance[port].mode = modeIdle
|
||||
coreInstance[port].dc = nil
|
||||
return nil, st
|
||||
}
|
||||
|
||||
case modeHost:
|
||||
// Allocate a free host controller and install interrupts
|
||||
hc, st := initHCI(port)
|
||||
if !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
// Initialize buffers and device descriptors
|
||||
if st = hc.init(); !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
coreInstance[port].port = port
|
||||
coreInstance[port].mode = mode
|
||||
coreInstance[port].hc = hc
|
||||
// Enable interrupts and enter runtime
|
||||
if st = hc.enable(true); !st.ok() {
|
||||
coreInstance[port].mode = modeIdle
|
||||
coreInstance[port].hc = nil
|
||||
return nil, st
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, statusInvalidArgument
|
||||
}
|
||||
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package usb2
|
||||
|
||||
// leU64 returns a slice containing 8 bytes from the given uint64 u.
|
||||
//
|
||||
// The returned bytes have little-endian ordering; that is, the first element
|
||||
// at index 0 is the least-significant byte in u and index 7 is the most-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func leU64(u uint64) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24),
|
||||
uint8(u >> 32), uint8(u >> 40), uint8(u >> 48), uint8(u >> 56),
|
||||
}
|
||||
}
|
||||
|
||||
// leU32 returns a slice containing 4 bytes from the given uint32 u.
|
||||
//
|
||||
// The returned bytes have little-endian ordering; that is, the first element
|
||||
// at index 0 is the least-significant byte in u and index 3 is the most-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func leU32(u uint32) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24),
|
||||
}
|
||||
}
|
||||
|
||||
// leU16 returns a slice containing 2 bytes from the given uint16 u.
|
||||
//
|
||||
// The returned bytes have little-endian ordering; that is, the first element
|
||||
// at index 0 is the least-significant byte in u and index 1 is the most-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func leU16(u uint16) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u), uint8(u >> 8),
|
||||
}
|
||||
}
|
||||
|
||||
// beU64 returns a slice containing 8 bytes from the given uint64 u.
|
||||
//
|
||||
// The returned bytes have big-endian ordering; that is, the first element at
|
||||
// index 0 is the most-significant byte in u and index 7 is the least-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func beU64(u uint64) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u >> 56), uint8(u >> 48), uint8(u >> 40), uint8(u >> 32),
|
||||
uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u),
|
||||
}
|
||||
}
|
||||
|
||||
// beU32 returns a slice containing 4 bytes from the given uint32 u.
|
||||
//
|
||||
// The returned bytes have big-endian ordering; that is, the first element at
|
||||
// index 0 is the most-significant byte in u and index 3 is the least-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func beU32(u uint32) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u),
|
||||
}
|
||||
}
|
||||
|
||||
// beU16 returns a slice containing 2 bytes from the given uint16 u.
|
||||
//
|
||||
// The returned bytes have big-endian ordering; that is, the first element at
|
||||
// index 0 is the most-significant byte in u and index 1 is the least-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func beU16(u uint16) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u >> 8), uint8(u),
|
||||
}
|
||||
}
|
||||
|
||||
// revU64 returns the given uint64 u with bytes in the reverse order.
|
||||
//go:inline
|
||||
func revU64(u uint64) uint64 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return 0
|
||||
}
|
||||
return ((u & 0x00000000000000FF) << 56) |
|
||||
((u & 0x000000000000FF00) << 40) |
|
||||
((u & 0x0000000000FF0000) << 24) |
|
||||
((u & 0x00000000FF000000) << 8) |
|
||||
((u & 0x000000FF00000000) >> 8) |
|
||||
((u & 0x0000FF0000000000) >> 24) |
|
||||
((u & 0x00FF000000000000) >> 40) |
|
||||
((u & 0xFF00000000000000) >> 56)
|
||||
}
|
||||
|
||||
// revU32 returns the given uint32 u with bytes in the reverse order.
|
||||
//go:inline
|
||||
func revU32(u uint32) uint32 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return 0
|
||||
}
|
||||
return ((u & 0x000000FF) << 24) | ((u & 0x0000FF00) << 8) |
|
||||
((u & 0x00FF0000) >> 8) | ((u & 0xFF000000) >> 24)
|
||||
}
|
||||
|
||||
// revU16 returns the given uint16 u with bytes in the reverse order.
|
||||
//go:inline
|
||||
func revU16(u uint16) uint16 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return 0
|
||||
}
|
||||
return ((u & 0x00FF) << 8) | ((u & 0xFF00) >> 8)
|
||||
}
|
||||
|
||||
// packU64 returns a uint64 constructed by concatenating the bytes in slice b.
|
||||
//
|
||||
// The least-significant byte in the returned value is the first element at
|
||||
// index 0 in b and the most significant byte is index 7, if given. If fewer
|
||||
// than 8 elements are given in b, the corresponding bytes in the returned value
|
||||
// are all 0.
|
||||
//go:inline
|
||||
func packU64(b []uint8) (u uint64) {
|
||||
for i := 0; i < 8 && i < len(b); i++ {
|
||||
u |= uint64(b[i]) << (i * 8)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// packU32 returns a uint32 constructed by concatenating the bytes in slice b.
|
||||
//
|
||||
// The least-significant byte in the returned value is the first element at
|
||||
// index 0 in b and the most significant byte is index 3, if given. If fewer
|
||||
// than 4 elements are given in b, the corresponding bytes in the returned value
|
||||
// are all 0.
|
||||
//go:inline
|
||||
func packU32(b []uint8) (u uint32) {
|
||||
for i := 0; i < 4 && i < len(b); i++ {
|
||||
u |= uint32(b[i]) << (i * 8)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// packU16 returns a uint16 constructed by concatenating the bytes in slice b.
|
||||
//
|
||||
// The least-significant byte in the returned value is the first element at
|
||||
// index 0 in b and the most significant byte is index 1, if given. If fewer
|
||||
// than 2 elements are given in b, the corresponding bytes in the returned value
|
||||
// are all 0.
|
||||
//go:inline
|
||||
func packU16(b []uint8) (u uint16) {
|
||||
for i := 0; i < 2 && i < len(b); i++ {
|
||||
u |= uint16(b[i]) << (i * 8)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// msU8 returns the most-significant byte of u.
|
||||
//go:inline
|
||||
func msU8(u uint16) uint8 { return uint8(u >> 8) }
|
||||
|
||||
// lsU8 returns the least-significant byte of u.
|
||||
//go:inline
|
||||
func lsU8(u uint16) uint8 { return uint8(u) }
|
||||
|
||||
// cycles converts the given number of microseconds to CPU cycles for a CPU with
|
||||
// given frequency.
|
||||
//go:inline
|
||||
func cycles(microsec, cpuFreqHz uint32) uint32 {
|
||||
return uint32((uint64(microsec) * uint64(cpuFreqHz)) / 1000000)
|
||||
}
|
||||
Reference in New Issue
Block a user