mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 10:37:46 +00:00
begin isolating target-specific USB code
This commit is contained in:
+558
-57
@@ -1,50 +1,74 @@
|
||||
package usb
|
||||
|
||||
// Implementation of 32-bit target-agnostic USB device controller driver (dcd).
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type dcd interface {
|
||||
class() class
|
||||
init() status
|
||||
enable(enable bool) status
|
||||
critical(enter bool) status
|
||||
interrupt()
|
||||
receive(endpoint uint8, transfer *dcdTransfer)
|
||||
transmit(endpoint uint8, transfer *dcdTransfer)
|
||||
control(setup dcdSetup)
|
||||
func init() {
|
||||
if unsafe.Sizeof(uintptr(0)) > 4 {
|
||||
panic("USB device controller is only supported on 32-bit systems")
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
dcdEndpointSize = 64 // bytes
|
||||
dcdTransferSize = 32 //
|
||||
dcdSetupSize = 8 //
|
||||
)
|
||||
// dcdCount defines the number of USB cores to configure for device mode. It is
|
||||
// computed as the sum of all declared device configuration descriptors.
|
||||
const dcdCount = descCDCACMCount // + ...
|
||||
|
||||
type dcdEndpoint struct {
|
||||
config uint32 // 4
|
||||
current *dcdTransfer // 4 *dcdTransfer
|
||||
transfer dcdTransfer // 32
|
||||
setup dcdSetup // 8
|
||||
// Endpoints are 48-byte data structures. The remaining data extends this to
|
||||
// 64-byte, and also makes it simpler for implementations to align endpoints
|
||||
// allocated contiguously on 64-byte boundaries.
|
||||
first *dcdTransfer // 4 *dcdTransfer
|
||||
last *dcdTransfer // 4 *dcdTransfer
|
||||
// After some discussion, perhaps the simplest change to support 64-bit (or
|
||||
// future TinyGo versions that don't use 8 bytes to refer to a function) is to
|
||||
// allocate a separate buffer of callbacks. The device controller would assign
|
||||
// callbacks to unused elements in that buffer, and only the index of that
|
||||
// callback would be stored here in the descriptor.
|
||||
callback dcdTransferCallback // 8
|
||||
// dcdInstance provides statically-allocated instances of each USB device
|
||||
// controller configured on this platform.
|
||||
var dcdInstance [dcdCount]dcd
|
||||
|
||||
// dhwInstance provides statically-allocated instances of each USB hardware
|
||||
// abstraction for ports configured as device on this platform.
|
||||
var dhwInstance [dcdCount]dhw
|
||||
|
||||
// dcd implements a generic USB device controller driver (dcd) for 32-bit ARM
|
||||
// targets.
|
||||
type dcd struct {
|
||||
*dhw // USB hardware abstraction layer
|
||||
|
||||
core *core // Parent USB core this instance is attached to
|
||||
port int // USB port index
|
||||
cc class // USB device class
|
||||
id int // USB device controller index
|
||||
}
|
||||
|
||||
type dcdTransferCallback func(transfer *dcdTransfer)
|
||||
type dcdTransfer struct {
|
||||
next *dcdTransfer // 4 *dcdTransfer
|
||||
token uint32 // 4
|
||||
pointer [5]uintptr // 20
|
||||
param uint32 // 4
|
||||
// initDCD 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 initDCD(port int, class class) (*dcd, status) {
|
||||
if 0 == dcdCount {
|
||||
return nil, statusInvalid // Must have defined device controllers
|
||||
}
|
||||
switch class.id {
|
||||
case classDeviceCDCACM:
|
||||
if 0 == class.config || class.config > descCDCACMCount {
|
||||
return nil, statusInvalid // Must have defined descriptors
|
||||
}
|
||||
default:
|
||||
}
|
||||
// Return the first instance whose assigned core is currently nil.
|
||||
for i := range dcdInstance {
|
||||
if nil == dcdInstance[i].core {
|
||||
// Initialize device controller.
|
||||
dcdInstance[i].dhw = allocDHW(port, i, &dcdInstance[i])
|
||||
dcdInstance[i].core = &coreInstance[port]
|
||||
dcdInstance[i].port = port
|
||||
dcdInstance[i].cc = class
|
||||
dcdInstance[i].id = i
|
||||
return &dcdInstance[i], statusOK
|
||||
}
|
||||
}
|
||||
return nil, statusBusy // No free device controller instances available.
|
||||
}
|
||||
|
||||
// class returns the receiver's current device class configuration.
|
||||
func (d *dcd) class() class { return d.cc }
|
||||
|
||||
// dcdSetupSize defines the size (bytes) of a USB standard setup packet.
|
||||
const dcdSetupSize = 8 // bytes
|
||||
|
||||
// dcdSetup contains the USB standard setup packet used to configure a device.
|
||||
type dcdSetup struct {
|
||||
bmRequestType uint8
|
||||
bRequest uint8
|
||||
@@ -53,30 +77,507 @@ type dcdSetup struct {
|
||||
wLength uint16
|
||||
}
|
||||
|
||||
// pack returns the receiver setup packet encoded as uint64.
|
||||
func (s dcdSetup) pack() uint64 {
|
||||
return ((uint64(s.bmRequestType) & 0xFF) << 0) | // uint8
|
||||
((uint64(s.bRequest) & 0xFF) << 8) | // uint8
|
||||
((uint64(s.wValue) & 0xFFFF) << 16) | // uint16
|
||||
((uint64(s.wIndex) & 0xFFFF) << 32) | // uint16
|
||||
((uint64(s.wLength) & 0xFFFF) << 48) // uint16
|
||||
return ((uint64(s.bmRequestType) & 0xFF) << 0) |
|
||||
((uint64(s.bRequest) & 0xFF) << 8) |
|
||||
((uint64(s.wValue) & 0xFFFF) << 16) |
|
||||
((uint64(s.wIndex) & 0xFFFF) << 32) |
|
||||
((uint64(s.wLength) & 0xFFFF) << 48)
|
||||
}
|
||||
|
||||
var (
|
||||
// dcdPointerNil is a sentinel value used to indicate a pointer to an invalid
|
||||
// value. Do not attempt to dereference!
|
||||
dcdPointerNil = uintptr(0)
|
||||
// dcdTransferEOL is a sentinel value used to indicate the final node in a
|
||||
// linked list of transfer descriptors.
|
||||
dcdTransferEOL = (*dcdTransfer)(unsafe.Pointer(dcdTransferPointerEOL))
|
||||
// dcdTransferPointerEOL is the notional memory address of dcdTransferEOL.
|
||||
// The address does not refer to an actual dcdTransfer, so no attempt should
|
||||
// be made to dereference it.
|
||||
dcdTransferPointerEOL = uintptr(1)
|
||||
// dcdEvent is used to describe virtual interrupts on the USB bus to a device
|
||||
// controller.
|
||||
//
|
||||
// Since the device controller software is intended for use with multiple TinyGo
|
||||
// targets, all of which may not have exactly the same USB bus interrupts, a
|
||||
// "virtual interrupt" is defined that is common to all targets. The target's
|
||||
// hardware implementation (type dhw) is responsible for translating real system
|
||||
// interrupts it receives into the appropriate virtual interrupt code, defined
|
||||
// below, and notifying the device controller via method (*dcd).event(dcdEvent).
|
||||
type dcdEvent struct {
|
||||
id uint8
|
||||
setup dcdSetup
|
||||
mask uint32
|
||||
}
|
||||
|
||||
// Enumerated constants for all possible USB device controller interrupt codes.
|
||||
const (
|
||||
dcdEventInvalid uint8 = iota // Invalid interrupt
|
||||
dcdEventStatusReset // USB reset received
|
||||
dcdEventStatusRun // USB controller entered run state
|
||||
dcdEventStatusSuspend // USB suspend received
|
||||
dcdEventStatusError // USB error condition detected on bus
|
||||
dcdEventControlSetup // USB setup received
|
||||
dcdEventPeripheralReady // USB PHY powered and ready to _go_
|
||||
dcdEventTransactComplete // USB transaction complete
|
||||
dcdEventTimer // USB (system) timer
|
||||
)
|
||||
|
||||
// nextTransfer returns the next transfer descriptor pointed to by the receiver
|
||||
// transfer descriptor, and whether or not that next descriptor is the final
|
||||
// descriptor in the list.
|
||||
func (t *dcdTransfer) nextTransfer() (*dcdTransfer, bool) {
|
||||
return t.next, uintptr(unsafe.Pointer(t.next)) == dcdTransferPointerEOL
|
||||
func (d *dcd) event(ev dcdEvent) {
|
||||
|
||||
switch ev.id {
|
||||
case dcdEventInvalid:
|
||||
case dcdEventStatusReset:
|
||||
d.endpointMask = 0
|
||||
|
||||
case dcdEventPeripheralReady:
|
||||
// Configure and enable control endpoint 0
|
||||
d.endpointEnable(0, true, 0)
|
||||
|
||||
case dcdEventStatusRun:
|
||||
case dcdEventStatusSuspend:
|
||||
case dcdEventStatusError:
|
||||
case dcdEventControlSetup:
|
||||
// On control endpoint 0 setup events, the ev.setup field will be defined
|
||||
switch d.controlSetup(ev.setup) {
|
||||
case dcdStageSetup:
|
||||
case dcdStageData:
|
||||
case dcdStageStatus:
|
||||
d.controlStatus()
|
||||
case dcdStageStall:
|
||||
d.controlStall()
|
||||
}
|
||||
|
||||
case dcdEventTransactComplete:
|
||||
case dcdEventTimer:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// dcdStage represents the USB transaction stage of a control request.
|
||||
type dcdStage uint8
|
||||
|
||||
// Enumerated constants for all possible USB transaction stages.
|
||||
const (
|
||||
dcdStageSetup dcdStage = iota // Indicates no stage transition required
|
||||
dcdStageData // IN/OUT data transfer
|
||||
dcdStageStatus // Setup request complete
|
||||
dcdStageStall // Unhandled or invalid request
|
||||
)
|
||||
|
||||
// controlSetup handles setup messages on control endpoint 0.
|
||||
func (d *dcd) controlSetup(sup dcdSetup) dcdStage {
|
||||
|
||||
// Reset endpoint 0 notify mask
|
||||
d.controlMask = 0
|
||||
|
||||
// First, switch on the type of request (standard, class, or vendor)
|
||||
switch sup.bmRequestType & descRequestTypeTypeMsk {
|
||||
|
||||
// === STANDARD REQUEST ===
|
||||
case descRequestTypeTypeStandard:
|
||||
|
||||
// Switch on the recepient and direction of the request
|
||||
switch sup.bmRequestType &
|
||||
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
|
||||
|
||||
// --- DEVICE Rx (OUT) ---
|
||||
case descRequestTypeRecipientDevice | descRequestTypeDirOut:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// SET ADDRESS (0x05):
|
||||
case descRequestStandardSetAddress:
|
||||
d.controlDeviceAddress(sup.wValue)
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageSetup
|
||||
|
||||
// SET CONFIGURATION (0x09):
|
||||
case descRequestStandardSetConfiguration:
|
||||
d.cc.config = int(sup.wValue)
|
||||
if 0 == d.cc.config || d.cc.config > dcdCount {
|
||||
// Use default if invalid index received
|
||||
d.cc.config = 1
|
||||
}
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
d.uartConfigure()
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- DEVICE Tx (IN) ---
|
||||
case descRequestTypeRecipientDevice | descRequestTypeDirIn:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// GET STATUS (0x00):
|
||||
case descRequestStandardGetStatus:
|
||||
d.controlReply[0] = 0
|
||||
d.controlReply[1] = 0
|
||||
d.controlTransmit(
|
||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
|
||||
return dcdStageSetup
|
||||
|
||||
// GET DESCRIPTOR (0x06):
|
||||
case descRequestStandardGetDescriptor:
|
||||
d.controlDescriptor(sup)
|
||||
return dcdStageSetup
|
||||
|
||||
// GET CONFIGURATION (0x08):
|
||||
case descRequestStandardGetConfiguration:
|
||||
d.controlReply[0] = uint8(d.cc.config)
|
||||
d.controlTransmit(
|
||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 1, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- INTERFACE Tx (IN) ---
|
||||
case descRequestTypeRecipientInterface | descRequestTypeDirIn:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// GET DESCRIPTOR (0x06):
|
||||
case descRequestStandardGetDescriptor:
|
||||
d.controlDescriptor(sup)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- ENDPOINT Rx (OUT) ---
|
||||
case descRequestTypeRecipientEndpoint | descRequestTypeDirOut:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// CLEAR FEATURE (0x01):
|
||||
case descRequestStandardClearFeature:
|
||||
// TODO
|
||||
|
||||
// SET FEATURE (0x03):
|
||||
case descRequestStandardSetFeature:
|
||||
// TODO
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
// --- ENDPOINT Tx (IN) ---
|
||||
case descRequestTypeRecipientEndpoint | descRequestTypeDirIn:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// GET STATUS (0x00):
|
||||
case descRequestStandardGetStatus:
|
||||
status := d.endpointStatus(uint8(sup.wIndex))
|
||||
d.controlReply[0] = uint8(status)
|
||||
d.controlReply[1] = uint8(status >> 8)
|
||||
d.controlTransmit(
|
||||
uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request recepient or direction
|
||||
}
|
||||
|
||||
// === CLASS REQUEST ===
|
||||
case descRequestTypeTypeClass:
|
||||
|
||||
// Switch on the recepient and direction of the request
|
||||
switch sup.bmRequestType &
|
||||
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
|
||||
|
||||
// --- INTERFACE Rx (OUT) ---
|
||||
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
|
||||
|
||||
// Identify which request was received
|
||||
switch sup.bRequest {
|
||||
|
||||
// CDC | SET LINE CODING (0x20):
|
||||
case descCDCRequestSetLineCoding:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
// line coding must contain exactly 7 bytes
|
||||
if descCDCACMCodingSize == sup.wLength {
|
||||
d.setup = sup
|
||||
d.controlReceive(
|
||||
uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].cx[0])),
|
||||
descCDCACMCodingSize, true)
|
||||
return dcdStageSetup
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
// CDC | SET CONTROL LINE STATE (0x22):
|
||||
case descCDCRequestSetControlLineState:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
|
||||
// Determine interface destination of the notification
|
||||
switch sup.wIndex {
|
||||
|
||||
// Control/status interface:
|
||||
case descCDCACMInterfaceCtrl:
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled device interface
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
// CDC | SEND BREAK (0x23):
|
||||
case descCDCRequestSendBreak:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
d.controlReceive(uintptr(0), 0, false)
|
||||
return dcdStageSetup
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request recepient or direction
|
||||
}
|
||||
|
||||
case descRequestTypeTypeVendor:
|
||||
default:
|
||||
// Unhandled request type
|
||||
}
|
||||
|
||||
// All successful requests return early. If we reach this point, the request
|
||||
// was invalid or unhandled. Stall the endpoint.
|
||||
return dcdStageStall
|
||||
}
|
||||
|
||||
// controlComplete handles the setup completion of control endpoint 0.
|
||||
func (d *dcd) controlComplete(status uint32) {
|
||||
|
||||
// Reset endpoint 0 notify mask
|
||||
d.controlMask = 0
|
||||
|
||||
// First, switch on the type of request (standard, class, or vendor)
|
||||
switch d.setup.bmRequestType & descRequestTypeTypeMsk {
|
||||
|
||||
// === CLASS REQUEST ===
|
||||
case descRequestTypeTypeClass:
|
||||
|
||||
// Switch on the recepient and direction of the request
|
||||
switch d.setup.bmRequestType &
|
||||
(descRequestTypeRecipientMsk | descRequestTypeDirMsk) {
|
||||
|
||||
// --- INTERFACE Rx (OUT) ---
|
||||
case descRequestTypeRecipientInterface | descRequestTypeDirOut:
|
||||
|
||||
// Identify which request was received
|
||||
switch d.setup.bRequest {
|
||||
|
||||
// CDC | SET LINE CODING (0x20):
|
||||
case descCDCRequestSetLineCoding:
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
acm := &descCDCACM[d.cc.config-1]
|
||||
|
||||
// Determine interface destination of the notification
|
||||
switch d.setup.wIndex {
|
||||
|
||||
// Control/status interface:
|
||||
case descCDCACMInterfaceCtrl:
|
||||
|
||||
// Notify PHY to handle triggers like special baud rates, which
|
||||
// signal to reboot into bootloader or begin receiving OTA updates
|
||||
d.controlLineCoding(descCDCACMLineCoding{
|
||||
baud: packU32(acm.cx[:]),
|
||||
stopBits: acm.cx[4],
|
||||
parity: acm.cx[5],
|
||||
numBits: acm.cx[6],
|
||||
})
|
||||
|
||||
default:
|
||||
// Unhandled device interface
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled recepient or direction
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled request type
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dcd) controlDescriptor(sup dcdSetup) {
|
||||
|
||||
// Respond based on our device class configuration
|
||||
switch d.cc.id {
|
||||
|
||||
// CDC-ACM (single)
|
||||
case classDeviceCDCACM:
|
||||
acm := &descCDCACM[d.cc.config-1]
|
||||
dxn := uint8(0)
|
||||
|
||||
// Determine the type of descriptor being requested
|
||||
switch sup.wValue >> 8 {
|
||||
|
||||
// Device descriptor
|
||||
case descTypeDevice:
|
||||
dxn = descLengthDevice
|
||||
_ = copy(acm.dx[:], acm.device[:dxn])
|
||||
|
||||
// Configuration descriptor
|
||||
case descTypeConfigure:
|
||||
dxn = uint8(descCDCACMConfigSize)
|
||||
_ = copy(acm.dx[:], acm.config[:dxn])
|
||||
|
||||
// String descriptor
|
||||
case descTypeString:
|
||||
if 0 == len(acm.locale) {
|
||||
break // No string descriptors defined!
|
||||
}
|
||||
var sd []uint8
|
||||
if 0 == uint8(sup.wValue) {
|
||||
|
||||
// setup.wIndex contains an arbitrary index referring to a collection of
|
||||
// strings in some given language. This case (setup.wValue = [0x03]00)
|
||||
// is a string request from the host to determine what that language is.
|
||||
//
|
||||
// In subsequent string requests, the host will populate setup.wIndex
|
||||
// with the language code we return here in this string descriptor.
|
||||
//
|
||||
// This way all strings returned to the host are in the same language,
|
||||
// whatever language that may be.
|
||||
code := int(sup.wIndex)
|
||||
if code >= len(acm.locale) {
|
||||
code = 0
|
||||
}
|
||||
sd = acm.locale[code].descriptor[sup.wValue&0xFF][:]
|
||||
|
||||
} else {
|
||||
|
||||
// setup.wIndex now contains a language code, which we specified 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 sup.wIndex == acm.locale[code].language {
|
||||
// Found language, check if string descriptor at given index exists
|
||||
if int(sup.wValue&0xFF) < len(acm.locale[code].descriptor) {
|
||||
|
||||
// Found language with a string defined at the requested index.
|
||||
//
|
||||
// TODO: Add API methods to device controller that allows the user
|
||||
// to provide these strings at/before driver initialization.
|
||||
//
|
||||
// For now, we just always use the descCommon* strings.
|
||||
var s string
|
||||
switch uint8(sup.wValue) {
|
||||
case 1:
|
||||
s = descCommonManufacturer
|
||||
case 2:
|
||||
s = descCommonProduct
|
||||
case 3:
|
||||
s = descCommonSerialNumber
|
||||
}
|
||||
|
||||
// Construct a string descriptor dynamically to be transmitted on
|
||||
// the serial bus.
|
||||
sd = acm.locale[code].descriptor[int(sup.wValue&0xFF)][:]
|
||||
// String descriptor format is 2-byte header + 2-bytes per rune
|
||||
sd[0] = uint8(2 + 2*len(s)) // header[0] = descriptor length
|
||||
sd[1] = descTypeString // header[1] = descriptor type
|
||||
// Copy UTF-8 string into string descriptor as UTF-16
|
||||
for n, c := range s {
|
||||
if 2+2*n >= len(sd) {
|
||||
break
|
||||
}
|
||||
sd[2+2*n] = uint8(c)
|
||||
sd[3+2*n] = 0
|
||||
}
|
||||
break // end search for matching language code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copy string descriptor into descriptor transmit buffer
|
||||
if nil != sd && len(sd) >= 0 {
|
||||
dxn = sd[0]
|
||||
_ = copy(acm.dx[:], sd[:dxn])
|
||||
}
|
||||
|
||||
// Device qualification descriptor
|
||||
case descTypeQualification:
|
||||
dxn = descLengthQualification
|
||||
_ = copy(acm.dx[:], acm.qualif[:dxn])
|
||||
|
||||
// Alternate configuration descriptor
|
||||
case descTypeOtherSpeedConfiguration:
|
||||
// TODO
|
||||
|
||||
default:
|
||||
// Unhandled descriptor type
|
||||
}
|
||||
|
||||
if dxn > 0 {
|
||||
if dxn > uint8(sup.wLength) {
|
||||
dxn = uint8(sup.wLength)
|
||||
}
|
||||
flushCache(
|
||||
uintptr(unsafe.Pointer(&acm.dx[0])), uintptr(dxn))
|
||||
d.controlTransmit(
|
||||
uintptr(unsafe.Pointer(&acm.dx[0])), uint32(dxn), false)
|
||||
}
|
||||
|
||||
default:
|
||||
// Unhandled device class
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+114
-76
@@ -135,6 +135,28 @@ const (
|
||||
descDeviceCapExtAttrBESLPos = 2
|
||||
)
|
||||
|
||||
const (
|
||||
// Attributes of all endpoint descriptor configurations.
|
||||
descEndptConfigAttr = descConfigAttrD7Msk | // Bit 7: reserved (1)
|
||||
(1 << descConfigAttrSelfPoweredPos) | // Bit 6: self-powered
|
||||
(0 << descConfigAttrRemoteWakeupPos) | // Bit 5: remote wakeup
|
||||
0 // Bits 0-4: reserved (0)
|
||||
|
||||
descEndptConfigAttrRxPos = 0
|
||||
descEndptConfigAttrTxPos = 16
|
||||
descEndptConfigAttrRxMsk = (descEndptConfigAttr | descEndptAttrSyncTypeMsk) << descEndptConfigAttrRxPos
|
||||
descEndptConfigAttrTxMsk = (descEndptConfigAttr | descEndptAttrSyncTypeMsk) << descEndptConfigAttrTxPos
|
||||
|
||||
descEndptConfigAttrRxUnused = 0x02 << descEndptConfigAttrRxPos
|
||||
descEndptConfigAttrTxUnused = 0x02 << descEndptConfigAttrTxPos
|
||||
descEndptConfigAttrRxIsochronous = (descEndptAttrSyncTypeAsync | descEndptConfigAttr) << descEndptConfigAttrRxPos
|
||||
descEndptConfigAttrTxIsochronous = (descEndptAttrSyncTypeAsync | descEndptConfigAttr) << descEndptConfigAttrTxPos
|
||||
descEndptConfigAttrRxBulk = (descEndptAttrSyncTypeAdaptive | descEndptConfigAttr) << descEndptConfigAttrRxPos
|
||||
descEndptConfigAttrTxBulk = (descEndptAttrSyncTypeAdaptive | descEndptConfigAttr) << descEndptConfigAttrTxPos
|
||||
descEndptConfigAttrRxInterrupt = (descEndptAttrSyncTypeSync | descEndptConfigAttr) << descEndptConfigAttrRxPos
|
||||
descEndptConfigAttrTxInterrupt = (descEndptAttrSyncTypeSync | descEndptConfigAttr) << descEndptConfigAttrTxPos
|
||||
)
|
||||
|
||||
// USB CDC constants defined per specification.
|
||||
const (
|
||||
|
||||
@@ -282,53 +304,6 @@ const (
|
||||
descCDCUARTStateOverrun = 0x40 // UART state OVERRUN
|
||||
)
|
||||
|
||||
// Common configuration constants for the USB CDC-ACM (single) device class.
|
||||
const (
|
||||
// String descriptor languages
|
||||
descCDCACMLanguageCount = 1
|
||||
// Interfaces for all CDC-ACM configurations.
|
||||
descCDCACMInterfaceCount = 2
|
||||
descCDCACMInterfaceCtrl = 0
|
||||
descCDCACMInterfaceData = 1
|
||||
// Endpoints for all CDC-ACM configurations.
|
||||
descCDCACMEndpointCount = 4
|
||||
descCDCACMEndpointStatus = 2 // Communication/control interrupt input
|
||||
descCDCACMEndpointDataRx = 3 // Bulk data output
|
||||
descCDCACMEndpointDataTx = 4 // Bulk data input
|
||||
// Endpoint configuration attributes for all CDC-ACM configurations.
|
||||
descCDCACMConfigAttrStatus = (descCDCACMConfigAttrUnused << descCDCACMConfigAttrRxPos) |
|
||||
(descCDCACMConfigAttrInterrupt << descCDCACMConfigAttrTxPos)
|
||||
descCDCACMConfigAttrDataRx = (descCDCACMConfigAttrBulk << descCDCACMConfigAttrRxPos) |
|
||||
(descCDCACMConfigAttrUnused << descCDCACMConfigAttrTxPos)
|
||||
descCDCACMConfigAttrDataTx = (descCDCACMConfigAttrUnused << descCDCACMConfigAttrRxPos) |
|
||||
(descCDCACMConfigAttrBulk << descCDCACMConfigAttrTxPos)
|
||||
|
||||
// Size of all CDC-ACM configuration descriptors.
|
||||
descCDCACMConfigSize = uint16(
|
||||
descLengthConfigure + // configuration
|
||||
descLengthInterface + // communication/control interface
|
||||
descCDCFuncLengthHeader + // CDC header
|
||||
descCDCFuncLengthCallManagement + // CDC call management
|
||||
descCDCFuncLengthAbstractControl + // CDC abstract control
|
||||
descCDCFuncLengthUnion + // 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)
|
||||
|
||||
descCDCACMConfigAttrRxPos = 0
|
||||
descCDCACMConfigAttrTxPos = 16
|
||||
descCDCACMConfigAttrUnused = 0x02 // TBD: what is this?
|
||||
descCDCACMConfigAttrIsochronous = descCDCACMConfigAttr | descEndptAttrSyncTypeAsync
|
||||
descCDCACMConfigAttrBulk = descCDCACMConfigAttr | descEndptAttrSyncTypeAdaptive
|
||||
descCDCACMConfigAttrInterrupt = descCDCACMConfigAttr | descEndptAttrSyncTypeSync
|
||||
)
|
||||
|
||||
// descCDCACM0Device holds the default device descriptor for CDC-ACM[0], i.e.,
|
||||
// configuration index 1.
|
||||
var descCDCACM0Device = [descLengthDevice]uint8{
|
||||
@@ -367,6 +342,21 @@ var descCDCACM0Qualif = [descLengthQualification]uint8{
|
||||
0, // Reserved
|
||||
}
|
||||
|
||||
const (
|
||||
// Size of all CDC-ACM configuration descriptors.
|
||||
descCDCACMConfigSize = uint16(
|
||||
descLengthConfigure + // configuration
|
||||
descLengthInterface + // communication/control interface
|
||||
descCDCFuncLengthHeader + // CDC header
|
||||
descCDCFuncLengthCallManagement + // CDC call management
|
||||
descCDCFuncLengthAbstractControl + // CDC abstract control
|
||||
descCDCFuncLengthUnion + // CDC union
|
||||
descLengthEndpoint + // communication/control input endpoint
|
||||
descLengthInterface + // data interface
|
||||
descLengthEndpoint + // data input endpoint
|
||||
descLengthEndpoint) // data output endpoint
|
||||
)
|
||||
|
||||
// descCDCACM0Config holds the default configuration descriptors for CDC-ACM[0],
|
||||
// i.e., configuration index 1.
|
||||
var descCDCACM0Config = [descCDCACMConfigSize]uint8{
|
||||
@@ -377,7 +367,7 @@ var descCDCACM0Config = [descCDCACMConfigSize]uint8{
|
||||
descCDCACMInterfaceCount, // 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
|
||||
descEndptConfigAttr, // Configuration attributes
|
||||
descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units)
|
||||
|
||||
// Communication/Control Interface Descriptor
|
||||
@@ -460,32 +450,6 @@ var descCDCACM0Config = [descCDCACMConfigSize]uint8{
|
||||
0, // Polling Interval
|
||||
}
|
||||
|
||||
// descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer.
|
||||
const descCDCACMCodingSize = 7
|
||||
|
||||
// descCDCACM0LineCoding holds the default UART line coding for CDC-ACM[0],
|
||||
// i.e., configuration index 1.
|
||||
var descCDCACM0LineCoding descCDCACMLineCoding
|
||||
|
||||
type descCDCACMLineCoding struct {
|
||||
baud uint32
|
||||
stopBits uint8
|
||||
parity uint8
|
||||
numBits uint8
|
||||
rtsdtr uint8
|
||||
}
|
||||
|
||||
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
|
||||
@@ -504,19 +468,93 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
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.
|
||||
)
|
||||
|
||||
// descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e.,
|
||||
// configuration index 1.
|
||||
var descCDCACM0String = [descCDCACMLanguageCount]descStringLanguage{
|
||||
{ // US English string descriptors
|
||||
|
||||
{ // [0x0409] US English
|
||||
language: descLanguageEnglish,
|
||||
descriptor: descStringIndex{
|
||||
{ // Language (index 0)
|
||||
{ /* [0] Language */
|
||||
4,
|
||||
descTypeString,
|
||||
lsU8(descLanguageEnglish),
|
||||
msU8(descLanguageEnglish),
|
||||
},
|
||||
// Actual string descriptors (index > 0) are copied into here at runtime!
|
||||
// This allows for application- or even user-defined string descriptors.
|
||||
{ /* [1] Manufacturer */ },
|
||||
{ /* [2] Product */ },
|
||||
{ /* [3] Serial Number */ },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer.
|
||||
const descCDCACMCodingSize = 7
|
||||
|
||||
// descCDCACMLineCoding represents an emulated UART's line configuration.
|
||||
type descCDCACMLineCoding struct {
|
||||
baud uint32
|
||||
stopBits uint8
|
||||
parity uint8
|
||||
numBits uint8
|
||||
}
|
||||
|
||||
// Common configuration constants for the USB CDC-ACM (single) device class.
|
||||
const (
|
||||
// String descriptor languages available
|
||||
descCDCACMLanguageCount = 1
|
||||
|
||||
// Interfaces for all CDC-ACM configurations.
|
||||
descCDCACMInterfaceCount = 2
|
||||
descCDCACMInterfaceCtrl = 0
|
||||
descCDCACMInterfaceData = 1
|
||||
|
||||
// Endpoints for all CDC-ACM configurations.
|
||||
descCDCACMEndpointCount = 4
|
||||
descCDCACMEndpointStatus = 2 // Communication/control interrupt input
|
||||
descCDCACMEndpointDataRx = 3 // Bulk data output
|
||||
descCDCACMEndpointDataTx = 4 // Bulk data input
|
||||
|
||||
// Endpoint configuration attributes for all CDC-ACM configurations.
|
||||
descCDCACMConfigAttrStatus = descEndptConfigAttrRxUnused | descEndptConfigAttrTxInterrupt
|
||||
descCDCACMConfigAttrDataRx = descEndptConfigAttrRxBulk | descEndptConfigAttrTxUnused
|
||||
descCDCACMConfigAttrDataTx = descEndptConfigAttrRxUnused | descEndptConfigAttrTxBulk
|
||||
)
|
||||
|
||||
// descCDCACMClass holds references to all descriptors, buffers, and control
|
||||
// structures for the USB CDC-ACM (single) device class.
|
||||
type descCDCACMClass struct {
|
||||
*descCDCACMClassData // Target-defined, class-specific data
|
||||
|
||||
locale *[descCDCACMLanguageCount]descStringLanguage // string descriptors
|
||||
device *[descLengthDevice]uint8 // device descriptor
|
||||
qualif *[descLengthQualification]uint8 // device qualification descriptor
|
||||
config *[descCDCACMConfigSize]uint8 // configuration descriptor
|
||||
}
|
||||
|
||||
// descCDCACM holds statically-allocated instances for each of the CDC-ACM
|
||||
// (single) device class configurations, ordered by index (offset by -1).
|
||||
var descCDCACM = [descCDCACMCount]descCDCACMClass{
|
||||
|
||||
{ // CDC-ACM (single) class configuration index 1
|
||||
descCDCACMClassData: &descCDCACMData[0],
|
||||
|
||||
locale: &descCDCACM0String,
|
||||
device: &descCDCACM0Device,
|
||||
qualif: &descCDCACM0Qualif,
|
||||
config: &descCDCACM0Config,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -69,33 +69,39 @@ const (
|
||||
// so the Device Controller can readily respond to incoming requests without
|
||||
// having to traverse a linked list.
|
||||
//go:align 4096
|
||||
var descCDCACM0QH [descCDCACMQHCount]dcdEndpoint
|
||||
var descCDCACM0QH [descCDCACMQHCount]dhwEndpoint
|
||||
|
||||
// descCDCACM0CD is the transfer descriptor for data messages transmitted or
|
||||
// received on the status/control endpoint 0 for the default CDC-ACM (single)
|
||||
// device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0CD dcdTransfer
|
||||
var descCDCACM0CD dhwTransfer
|
||||
|
||||
// descCDCACM0AD is the transfer descriptor for ackowledgement (ACK) messages
|
||||
// transmitted or received on the status/control endpoint 0 for the default
|
||||
// CDC-ACM (single) device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0AD dcdTransfer
|
||||
var descCDCACM0AD dhwTransfer
|
||||
|
||||
// descCDCACM0RD is an array of transfer descriptors for Rx (OUT) transfers,
|
||||
// which describe to the device controller the location and quantity of data
|
||||
// being received for a given transfer, for the default CDC-ACM (single) device
|
||||
// class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0RD [descCDCACMRDCount]dcdTransfer
|
||||
var descCDCACM0RD [descCDCACMRDCount]dhwTransfer
|
||||
|
||||
// descCDCACM0TD is an array of transfer descriptors for Tx (IN) transfers,
|
||||
// which describe to the device controller the location and quantity of data
|
||||
// being transmitted for a given transfer, for the default CDC-ACM (single)
|
||||
// device class configuration (index 1).
|
||||
//go:align 32
|
||||
var descCDCACM0TD [descCDCACMTDCount]dcdTransfer
|
||||
var descCDCACM0TD [descCDCACMTDCount]dhwTransfer
|
||||
|
||||
// descCDCACM0LineCoding holds the emulated UART line coding for the default
|
||||
// CDC-ACM (single) device class configuration (index 1).
|
||||
// i.e., configuration index 1.
|
||||
//go:align 32
|
||||
var descCDCACM0LC descCDCACMLineCoding
|
||||
|
||||
// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of
|
||||
// the default CDC-ACM (single) device class configuration (index 1).
|
||||
@@ -120,21 +126,22 @@ var descCDCACM0RDNum [descCDCACMRDCount]uint16
|
||||
var descCDCACM0RDIdx [descCDCACMRDCount]uint16
|
||||
var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16
|
||||
|
||||
type descCDCACMClass struct {
|
||||
locale *[descCDCACMLanguageCount]descStringLanguage // string descriptors
|
||||
device *[descLengthDevice]uint8 // device descriptor
|
||||
qualif *[descLengthQualification]uint8 // device qualification descriptor
|
||||
config *[descCDCACMConfigSize]uint8 // configuration descriptor
|
||||
// descCDCACM holds the buffers and control states for all of the CDC-ACM
|
||||
// (single) device class configurations, ordered by index (offset by -1), for
|
||||
// iMXRT1062 targets only.
|
||||
//
|
||||
// Instances of this type (elements of descCDCACMData) are embedded in elements
|
||||
// of the common/target-agnostic CDC-ACM class configurations (descCDCACM).
|
||||
// Methods defined on this type implement target-specific functionality, and
|
||||
// some of these methods are required by the common device controller driver.
|
||||
// Thus, this type functions as an additional hardware abstraction layer.
|
||||
type descCDCACMClassData struct {
|
||||
qh *[descCDCACMQHCount]dhwEndpoint // endpoint queue heads
|
||||
|
||||
lineCoding *descCDCACMLineCoding // UART line coding active state
|
||||
// lineActive int64 // time since last UART DTR/RTS
|
||||
|
||||
qh *[descCDCACMQHCount]dcdEndpoint // endpoint queue heads
|
||||
|
||||
cd *dcdTransfer // control endpoint 0 Rx/Tx data transfer descriptor
|
||||
ad *dcdTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
|
||||
rd *[descCDCACMRDCount]dcdTransfer // bulk data endpoint Rx (OUT) transfer descriptors
|
||||
td *[descCDCACMTDCount]dcdTransfer // bulk data endpoint Tx (IN) transfer descriptors
|
||||
cd *dhwTransfer // control endpoint 0 Rx/Tx data transfer descriptor
|
||||
ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
|
||||
rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors
|
||||
td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors
|
||||
|
||||
cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
|
||||
rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer
|
||||
@@ -156,21 +163,18 @@ type descCDCACMClass struct {
|
||||
rxCount *[descCDCACMRDCount]uint16
|
||||
rxIndex *[descCDCACMRDCount]uint16
|
||||
rxQueue *[descCDCACMRDCount + 1]uint16
|
||||
|
||||
_ [2]uint8
|
||||
}
|
||||
|
||||
// descCDCACM holds the configuration, endpoint, and transfer descriptors, along
|
||||
// with the buffers and control states, for all of the CDC-ACM (single) device
|
||||
// class configurations, ordered by configuration index (offset by -1).
|
||||
// descCDCACMData holds statically-allocated instances for each of the target-
|
||||
// specific (iMXRT1062) CDC-ACM (single) device class configurations' control
|
||||
// and data structures, ordered by configuration index (offset by -1). Each
|
||||
// element is embedded in a corresponding element of descCDCACM.
|
||||
//go:align 32
|
||||
var descCDCACM = [descCDCACMCount]descCDCACMClass{
|
||||
{
|
||||
locale: &descCDCACM0String,
|
||||
device: &descCDCACM0Device,
|
||||
qualif: &descCDCACM0Qualif,
|
||||
config: &descCDCACM0Config,
|
||||
|
||||
lineCoding: &descCDCACM0LineCoding,
|
||||
var descCDCACMData = [descCDCACMCount]descCDCACMClassData{
|
||||
|
||||
{ // CDC-ACM (single) class configuration index 1 data
|
||||
qh: &descCDCACM0QH,
|
||||
|
||||
cd: &descCDCACM0CD,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+50
-7
@@ -1,10 +1,53 @@
|
||||
package usb
|
||||
|
||||
type hcd interface {
|
||||
class() class
|
||||
init() status
|
||||
enable(enable bool) status
|
||||
critical(enter bool) status
|
||||
interrupt()
|
||||
udelay(micros uint32)
|
||||
// Implementation of 32-bit target-agnostic USB host controller driver (hcd).
|
||||
|
||||
// hcdCount defines the number of USB cores to configure for host mode. It is
|
||||
// computed as the sum of all declared host configuration descriptors.
|
||||
const hcdCount = 0 // + ...
|
||||
|
||||
// hcdInstance provides statically-allocated instances of each USB host
|
||||
// controller configured on this platform.
|
||||
var hcdInstance [hcdCount]hcd
|
||||
|
||||
// hhwInstance provides statically-allocated instances of each USB hardware
|
||||
// abstraction for ports configured as host on this platform.
|
||||
var hhwInstance [hcdCount]hhw
|
||||
|
||||
// hcd implements USB host controller driver (hcd) interface.
|
||||
type hcd struct {
|
||||
*hhw // USB hardware abstraction layer
|
||||
|
||||
core *core // Parent USB core this instance is attached to
|
||||
port int // USB port index
|
||||
cc class // USB host class
|
||||
id int // USB host controller index
|
||||
}
|
||||
|
||||
// initHCD 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 initHCD(port int, class class) (*hcd, status) {
|
||||
if 0 == hcdCount {
|
||||
return nil, statusInvalid // Must have defined host controllers
|
||||
}
|
||||
switch class.id {
|
||||
default:
|
||||
}
|
||||
// Return the first instance whose assigned core is currently nil.
|
||||
for i := range hcdInstance {
|
||||
if nil == hcdInstance[i].core {
|
||||
// Initialize host controller.
|
||||
hcdInstance[i].hhw = allocHHW(port, i, &hcdInstance[i])
|
||||
hcdInstance[i].core = &coreInstance[port]
|
||||
hcdInstance[i].port = port
|
||||
hcdInstance[i].cc = class
|
||||
hcdInstance[i].id = i
|
||||
return &hcdInstance[i], statusOK
|
||||
}
|
||||
}
|
||||
return nil, statusBusy // No free host controller instances available.
|
||||
}
|
||||
|
||||
// class returns the receiver's current host class configuration.
|
||||
func (h *hcd) class() class { return h.cc }
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb
|
||||
|
||||
// Implementation of USB host controller driver (hcd) for NXP iMXRT1062.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nxp"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
)
|
||||
|
||||
// hcdCount defines the number of USB cores to configure for host mode. It is
|
||||
// computed as the sum of all declared host configuration descriptors.
|
||||
const hcdCount = 0
|
||||
|
||||
// hcdInterruptPriority defines the priority for all USB host interrupts.
|
||||
const hcdInterruptPriority = 3
|
||||
|
||||
// hostController implements USB host controller driver (hcd) interface.
|
||||
type hostController struct {
|
||||
core *core // Parent USB core this instance is attached to
|
||||
port int // USB port index
|
||||
cc class // USB host class
|
||||
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 [hcdCount]hostController
|
||||
|
||||
// initHCD 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 initHCD(port int, class class) (hcd, status) {
|
||||
if 0 == hcdCount {
|
||||
return nil, statusInvalid // must have defined host controllers
|
||||
}
|
||||
// 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].cc = class
|
||||
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) class() class { return hc.cc }
|
||||
|
||||
func (hc *hostController) init() status {
|
||||
|
||||
return statusOK
|
||||
}
|
||||
|
||||
func (hc *hostController) enable(enable bool) status {
|
||||
|
||||
hc.irq.SetPriority(hcdInterruptPriority)
|
||||
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,59 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb
|
||||
|
||||
// Implementation of USB host controller driver (hcd) for NXP iMXRT1062.
|
||||
|
||||
import (
|
||||
"device/nxp"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
// hcdInterruptPriority defines the priority for all USB host interrupts.
|
||||
const hcdInterruptPriority = 3
|
||||
|
||||
// hcd implements USB host controller driver (hcd) interface.
|
||||
type hhw struct {
|
||||
*hcd // USB host controller driver
|
||||
|
||||
bus *nxp.USB_Type // USB core register
|
||||
phy *nxp.USBPHY_Type // USB PHY register
|
||||
irq interrupt.Interrupt // USB IRQ, only a single interrupt on iMXRT1062
|
||||
}
|
||||
|
||||
// allocHHW returns a reference to the USB hardware abstraction for the given
|
||||
// host controller driver. Should be called only one time and during host
|
||||
// controller initialization.
|
||||
func allocHHW(port, instance int, hc *hcd) *hhw {
|
||||
switch port {
|
||||
case 0:
|
||||
hhwInstance[instance].hcd = hc
|
||||
hhwInstance[instance].bus = nxp.USB1
|
||||
hhwInstance[instance].phy = nxp.USBPHY1
|
||||
|
||||
case 1:
|
||||
hhwInstance[instance].hcd = hc
|
||||
hhwInstance[instance].bus = nxp.USB2
|
||||
hhwInstance[instance].phy = nxp.USBPHY2
|
||||
}
|
||||
|
||||
return &hhwInstance[instance]
|
||||
}
|
||||
|
||||
// init configures the USB port for host mode operation by initializing all
|
||||
// endpoint and transfer descriptor data structures, initializing core registers
|
||||
// and interrupts, resetting the USB PHY, and enabling power on the bust.
|
||||
func (h *hhw) init() status {
|
||||
|
||||
return statusOK
|
||||
}
|
||||
|
||||
// enable causes the USB core to enter (or exit) the normal run state and
|
||||
// enables/disables all interrupts on the receiver's USB port.
|
||||
func (h *hhw) enable(enable bool) {
|
||||
if enable {
|
||||
h.irq.Enable() // Enable USB interrupts
|
||||
} else {
|
||||
h.irq.Disable() // Disable USB interrupts
|
||||
}
|
||||
}
|
||||
+5
-25
@@ -41,21 +41,13 @@ func (uart *UART) Configure(config UARTConfig) error {
|
||||
|
||||
// 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()
|
||||
return uart.core.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()
|
||||
n, ok := uart.core.dc.uartReadByte()
|
||||
if !ok {
|
||||
return 0, ErrUARTEmptyBuffer
|
||||
}
|
||||
@@ -64,20 +56,12 @@ func (uart UART) ReadByte() (byte, error) {
|
||||
|
||||
// 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
|
||||
return uart.core.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) {
|
||||
if !uart.core.dc.uartWriteByte(c) {
|
||||
return ErrUARTWriteFailed
|
||||
}
|
||||
return nil
|
||||
@@ -85,9 +69,5 @@ func (uart UART) WriteByte(c byte) error {
|
||||
|
||||
// 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
|
||||
return uart.core.dc.uartWrite(data), nil
|
||||
}
|
||||
|
||||
+64
-79
@@ -2,59 +2,6 @@ package usb
|
||||
|
||||
// Hardware abstraction for USB ports configured as either host or device.
|
||||
|
||||
import "unsafe"
|
||||
|
||||
func init() {
|
||||
if unsafe.Sizeof(uintptr(0)) > 4 {
|
||||
panic("USB is only supported on 32-bit systems")
|
||||
}
|
||||
}
|
||||
|
||||
// core represents the core of a USB port configured as either host or device.
|
||||
type core struct {
|
||||
port int
|
||||
mode int
|
||||
dc dcd
|
||||
hc hcd
|
||||
}
|
||||
|
||||
// Constant definitions for USB core operating modes.
|
||||
const (
|
||||
modeIdle = 0
|
||||
modeDevice = 1
|
||||
modeHost = 2
|
||||
)
|
||||
|
||||
// class represents the type of a host/device and its class configuration index.
|
||||
// The first valid configuration index is 1. Index 0 is reserved and invalid.
|
||||
type class struct {
|
||||
id int
|
||||
config int
|
||||
}
|
||||
|
||||
// Constant definitions for all host/device classes.
|
||||
const (
|
||||
classDeviceCDCACM = 0 // The only currently-supported class (CDC-ACM)
|
||||
)
|
||||
|
||||
// mode returns the USB core operating mode of the receiver class cl.
|
||||
//go:inline
|
||||
func (cl class) mode() int {
|
||||
switch cl.id {
|
||||
case classDeviceCDCACM:
|
||||
return modeDevice
|
||||
default:
|
||||
return modeIdle
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// host mode.
|
||||
const CoreCount = dcdCount + hcdCount
|
||||
@@ -63,26 +10,29 @@ const CoreCount = dcdCount + hcdCount
|
||||
// configured on this platform.
|
||||
var coreInstance [CoreCount]core
|
||||
|
||||
// status represents the return code of a subroutine.
|
||||
type status uint8
|
||||
// core represents the core of a USB port configured as either host or device.
|
||||
type core struct {
|
||||
port int
|
||||
mode int
|
||||
dc *dcd
|
||||
hc *hcd
|
||||
}
|
||||
|
||||
// Constant definitions for all status codes used within the package.
|
||||
// Constant definitions for USB core operating modes.
|
||||
const (
|
||||
statusOK status = iota // Success
|
||||
statusBusy // Busy
|
||||
statusRetry // Retry
|
||||
statusInvalid // Invalid argument
|
||||
modeIdle = 0 // USB port has not been configured
|
||||
modeDevice = 1
|
||||
modeHost = 2
|
||||
)
|
||||
|
||||
// ok returns true if and only if the receiver st equals statusOK.
|
||||
//go:inline
|
||||
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 int, class class) (*core, status) {
|
||||
|
||||
iv := disableInterrupts()
|
||||
defer enableInterrupts(iv)
|
||||
|
||||
if port < 0 || port >= CoreCount || 0 == class.config {
|
||||
return nil, statusInvalid
|
||||
}
|
||||
@@ -90,9 +40,10 @@ func initCore(port int, class class) (*core, status) {
|
||||
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.
|
||||
//
|
||||
// This will allow, for instance, 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:
|
||||
@@ -122,12 +73,7 @@ func initCore(port int, class class) (*core, status) {
|
||||
coreInstance[port].port = port
|
||||
coreInstance[port].mode = modeDevice
|
||||
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
|
||||
}
|
||||
dc.enable(true) // Enable interrupts and enter runtime
|
||||
|
||||
case modeHost:
|
||||
// Allocate a free host controller and install interrupts
|
||||
@@ -142,12 +88,7 @@ func initCore(port int, class class) (*core, status) {
|
||||
coreInstance[port].port = port
|
||||
coreInstance[port].mode = modeHost
|
||||
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
|
||||
}
|
||||
hc.enable(true) // Enable interrupts and enter runtime
|
||||
|
||||
default:
|
||||
return nil, statusInvalid
|
||||
@@ -155,3 +96,47 @@ func initCore(port int, class class) (*core, status) {
|
||||
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
|
||||
// class represents the type of a host/device and its class configuration index.
|
||||
// The first valid configuration index is 1. Index 0 is reserved and invalid.
|
||||
type class struct {
|
||||
id int
|
||||
config int
|
||||
}
|
||||
|
||||
// Enumerated constants for all host/device class configurations.
|
||||
const (
|
||||
classDeviceCDCACM = 0 // The only currently-supported class (CDC-ACM)
|
||||
)
|
||||
|
||||
// mode returns the USB core operating mode of the receiver class c.
|
||||
//go:inline
|
||||
func (c class) mode() int {
|
||||
switch c.id {
|
||||
case classDeviceCDCACM:
|
||||
return modeDevice
|
||||
default:
|
||||
return modeIdle
|
||||
}
|
||||
}
|
||||
|
||||
// equals returns true if and only if all fields of the given class are equal to
|
||||
// those of the receiver c.
|
||||
//go:inline
|
||||
func (c class) equals(class class) bool {
|
||||
return c.id == class.id && c.config == class.config
|
||||
}
|
||||
|
||||
// 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
|
||||
statusInvalid // Invalid argument
|
||||
)
|
||||
|
||||
// ok returns true if and only if the receiver st equals statusOK.
|
||||
//go:inline
|
||||
func (s status) ok() bool { return statusOK == s }
|
||||
|
||||
@@ -4,6 +4,9 @@ package usb
|
||||
|
||||
import "device/arm"
|
||||
|
||||
//go:linkname ticks runtime.ticks
|
||||
func ticks() int64
|
||||
|
||||
// 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.
|
||||
@@ -14,3 +17,11 @@ func udelay(microsec uint32) {
|
||||
arm.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
|
||||
func disableInterrupts() uintptr {
|
||||
return arm.DisableInterrupts()
|
||||
}
|
||||
|
||||
func enableInterrupts(mask uintptr) {
|
||||
arm.EnableInterrupts(mask)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user