begin isolating target-specific USB code

This commit is contained in:
ardnew
2021-04-27 10:14:23 -05:00
parent 0ff290cecc
commit 43d5eee1e6
12 changed files with 1968 additions and 1742 deletions
+558 -57
View File
@@ -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
View File
@@ -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,
},
}
+34 -30
View File
@@ -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
View File
@@ -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 }
-130
View File
@@ -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`)
}
}
+59
View File
@@ -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
View File
@@ -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
View File
@@ -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 }
+11
View File
@@ -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)
}