mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-12 15:03:41 +00:00
begin isolating target-specific USB code
This commit is contained in:
+558
-57
@@ -1,50 +1,74 @@
|
|||||||
package usb
|
package usb
|
||||||
|
|
||||||
|
// Implementation of 32-bit target-agnostic USB device controller driver (dcd).
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
type dcd interface {
|
func init() {
|
||||||
class() class
|
if unsafe.Sizeof(uintptr(0)) > 4 {
|
||||||
init() status
|
panic("USB device controller is only supported on 32-bit systems")
|
||||||
enable(enable bool) status
|
}
|
||||||
critical(enter bool) status
|
|
||||||
interrupt()
|
|
||||||
receive(endpoint uint8, transfer *dcdTransfer)
|
|
||||||
transmit(endpoint uint8, transfer *dcdTransfer)
|
|
||||||
control(setup dcdSetup)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
// dcdCount defines the number of USB cores to configure for device mode. It is
|
||||||
dcdEndpointSize = 64 // bytes
|
// computed as the sum of all declared device configuration descriptors.
|
||||||
dcdTransferSize = 32 //
|
const dcdCount = descCDCACMCount // + ...
|
||||||
dcdSetupSize = 8 //
|
|
||||||
)
|
|
||||||
|
|
||||||
type dcdEndpoint struct {
|
// dcdInstance provides statically-allocated instances of each USB device
|
||||||
config uint32 // 4
|
// controller configured on this platform.
|
||||||
current *dcdTransfer // 4 *dcdTransfer
|
var dcdInstance [dcdCount]dcd
|
||||||
transfer dcdTransfer // 32
|
|
||||||
setup dcdSetup // 8
|
// dhwInstance provides statically-allocated instances of each USB hardware
|
||||||
// Endpoints are 48-byte data structures. The remaining data extends this to
|
// abstraction for ports configured as device on this platform.
|
||||||
// 64-byte, and also makes it simpler for implementations to align endpoints
|
var dhwInstance [dcdCount]dhw
|
||||||
// allocated contiguously on 64-byte boundaries.
|
|
||||||
first *dcdTransfer // 4 *dcdTransfer
|
// dcd implements a generic USB device controller driver (dcd) for 32-bit ARM
|
||||||
last *dcdTransfer // 4 *dcdTransfer
|
// targets.
|
||||||
// After some discussion, perhaps the simplest change to support 64-bit (or
|
type dcd struct {
|
||||||
// future TinyGo versions that don't use 8 bytes to refer to a function) is to
|
*dhw // USB hardware abstraction layer
|
||||||
// allocate a separate buffer of callbacks. The device controller would assign
|
|
||||||
// callbacks to unused elements in that buffer, and only the index of that
|
core *core // Parent USB core this instance is attached to
|
||||||
// callback would be stored here in the descriptor.
|
port int // USB port index
|
||||||
callback dcdTransferCallback // 8
|
cc class // USB device class
|
||||||
|
id int // USB device controller index
|
||||||
}
|
}
|
||||||
|
|
||||||
type dcdTransferCallback func(transfer *dcdTransfer)
|
// initDCD initializes and assigns a free device controller instance to the
|
||||||
type dcdTransfer struct {
|
// given USB port. Returns the initialized device controller or nil if no free
|
||||||
next *dcdTransfer // 4 *dcdTransfer
|
// device controller instances remain.
|
||||||
token uint32 // 4
|
func initDCD(port int, class class) (*dcd, status) {
|
||||||
pointer [5]uintptr // 20
|
if 0 == dcdCount {
|
||||||
param uint32 // 4
|
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 {
|
type dcdSetup struct {
|
||||||
bmRequestType uint8
|
bmRequestType uint8
|
||||||
bRequest uint8
|
bRequest uint8
|
||||||
@@ -53,30 +77,507 @@ type dcdSetup struct {
|
|||||||
wLength uint16
|
wLength uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pack returns the receiver setup packet encoded as uint64.
|
||||||
func (s dcdSetup) pack() uint64 {
|
func (s dcdSetup) pack() uint64 {
|
||||||
return ((uint64(s.bmRequestType) & 0xFF) << 0) | // uint8
|
return ((uint64(s.bmRequestType) & 0xFF) << 0) |
|
||||||
((uint64(s.bRequest) & 0xFF) << 8) | // uint8
|
((uint64(s.bRequest) & 0xFF) << 8) |
|
||||||
((uint64(s.wValue) & 0xFFFF) << 16) | // uint16
|
((uint64(s.wValue) & 0xFFFF) << 16) |
|
||||||
((uint64(s.wIndex) & 0xFFFF) << 32) | // uint16
|
((uint64(s.wIndex) & 0xFFFF) << 32) |
|
||||||
((uint64(s.wLength) & 0xFFFF) << 48) // uint16
|
((uint64(s.wLength) & 0xFFFF) << 48)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
// dcdEvent is used to describe virtual interrupts on the USB bus to a device
|
||||||
// dcdPointerNil is a sentinel value used to indicate a pointer to an invalid
|
// controller.
|
||||||
// value. Do not attempt to dereference!
|
//
|
||||||
dcdPointerNil = uintptr(0)
|
// Since the device controller software is intended for use with multiple TinyGo
|
||||||
// dcdTransferEOL is a sentinel value used to indicate the final node in a
|
// targets, all of which may not have exactly the same USB bus interrupts, a
|
||||||
// linked list of transfer descriptors.
|
// "virtual interrupt" is defined that is common to all targets. The target's
|
||||||
dcdTransferEOL = (*dcdTransfer)(unsafe.Pointer(dcdTransferPointerEOL))
|
// hardware implementation (type dhw) is responsible for translating real system
|
||||||
// dcdTransferPointerEOL is the notional memory address of dcdTransferEOL.
|
// interrupts it receives into the appropriate virtual interrupt code, defined
|
||||||
// The address does not refer to an actual dcdTransfer, so no attempt should
|
// below, and notifying the device controller via method (*dcd).event(dcdEvent).
|
||||||
// be made to dereference it.
|
type dcdEvent struct {
|
||||||
dcdTransferPointerEOL = uintptr(1)
|
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
|
func (d *dcd) event(ev dcdEvent) {
|
||||||
// transfer descriptor, and whether or not that next descriptor is the final
|
|
||||||
// descriptor in the list.
|
switch ev.id {
|
||||||
func (t *dcdTransfer) nextTransfer() (*dcdTransfer, bool) {
|
case dcdEventInvalid:
|
||||||
return t.next, uintptr(unsafe.Pointer(t.next)) == dcdTransferPointerEOL
|
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
|
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.
|
// USB CDC constants defined per specification.
|
||||||
const (
|
const (
|
||||||
|
|
||||||
@@ -282,53 +304,6 @@ const (
|
|||||||
descCDCUARTStateOverrun = 0x40 // UART state OVERRUN
|
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.,
|
// descCDCACM0Device holds the default device descriptor for CDC-ACM[0], i.e.,
|
||||||
// configuration index 1.
|
// configuration index 1.
|
||||||
var descCDCACM0Device = [descLengthDevice]uint8{
|
var descCDCACM0Device = [descLengthDevice]uint8{
|
||||||
@@ -367,6 +342,21 @@ var descCDCACM0Qualif = [descLengthQualification]uint8{
|
|||||||
0, // Reserved
|
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],
|
// descCDCACM0Config holds the default configuration descriptors for CDC-ACM[0],
|
||||||
// i.e., configuration index 1.
|
// i.e., configuration index 1.
|
||||||
var descCDCACM0Config = [descCDCACMConfigSize]uint8{
|
var descCDCACM0Config = [descCDCACMConfigSize]uint8{
|
||||||
@@ -377,7 +367,7 @@ var descCDCACM0Config = [descCDCACMConfigSize]uint8{
|
|||||||
descCDCACMInterfaceCount, // Number of interfaces supported by this configuration
|
descCDCACMInterfaceCount, // Number of interfaces supported by this configuration
|
||||||
1, // Value to use to select this configuration (1 = CDC-ACM[0])
|
1, // Value to use to select this configuration (1 = CDC-ACM[0])
|
||||||
0, // Index of string descriptor describing this configuration
|
0, // Index of string descriptor describing this configuration
|
||||||
descCDCACMConfigAttr, // Configuration attributes
|
descEndptConfigAttr, // Configuration attributes
|
||||||
descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units)
|
descCDCACMMaxPower, // Max power consumption when fully-operational (2 mA units)
|
||||||
|
|
||||||
// Communication/Control Interface Descriptor
|
// Communication/Control Interface Descriptor
|
||||||
@@ -460,32 +450,6 @@ var descCDCACM0Config = [descCDCACMConfigSize]uint8{
|
|||||||
0, // Polling Interval
|
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 (
|
type (
|
||||||
// descString is the actual byte array used to hold string descriptors. The
|
// 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
|
// 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.,
|
// descCDCACM0String holds the default string descriptors for CDC-ACM[0], i.e.,
|
||||||
// configuration index 1.
|
// configuration index 1.
|
||||||
var descCDCACM0String = [descCDCACMLanguageCount]descStringLanguage{
|
var descCDCACM0String = [descCDCACMLanguageCount]descStringLanguage{
|
||||||
{ // US English string descriptors
|
|
||||||
|
{ // [0x0409] US English
|
||||||
language: descLanguageEnglish,
|
language: descLanguageEnglish,
|
||||||
descriptor: descStringIndex{
|
descriptor: descStringIndex{
|
||||||
{ // Language (index 0)
|
{ /* [0] Language */
|
||||||
4,
|
4,
|
||||||
descTypeString,
|
descTypeString,
|
||||||
lsU8(descLanguageEnglish),
|
lsU8(descLanguageEnglish),
|
||||||
msU8(descLanguageEnglish),
|
msU8(descLanguageEnglish),
|
||||||
},
|
},
|
||||||
// Actual string descriptors (index > 0) are copied into here at runtime!
|
// 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
|
// so the Device Controller can readily respond to incoming requests without
|
||||||
// having to traverse a linked list.
|
// having to traverse a linked list.
|
||||||
//go:align 4096
|
//go:align 4096
|
||||||
var descCDCACM0QH [descCDCACMQHCount]dcdEndpoint
|
var descCDCACM0QH [descCDCACMQHCount]dhwEndpoint
|
||||||
|
|
||||||
// descCDCACM0CD is the transfer descriptor for data messages transmitted or
|
// descCDCACM0CD is the transfer descriptor for data messages transmitted or
|
||||||
// received on the status/control endpoint 0 for the default CDC-ACM (single)
|
// received on the status/control endpoint 0 for the default CDC-ACM (single)
|
||||||
// device class configuration (index 1).
|
// device class configuration (index 1).
|
||||||
//go:align 32
|
//go:align 32
|
||||||
var descCDCACM0CD dcdTransfer
|
var descCDCACM0CD dhwTransfer
|
||||||
|
|
||||||
// descCDCACM0AD is the transfer descriptor for ackowledgement (ACK) messages
|
// descCDCACM0AD is the transfer descriptor for ackowledgement (ACK) messages
|
||||||
// transmitted or received on the status/control endpoint 0 for the default
|
// transmitted or received on the status/control endpoint 0 for the default
|
||||||
// CDC-ACM (single) device class configuration (index 1).
|
// CDC-ACM (single) device class configuration (index 1).
|
||||||
//go:align 32
|
//go:align 32
|
||||||
var descCDCACM0AD dcdTransfer
|
var descCDCACM0AD dhwTransfer
|
||||||
|
|
||||||
// descCDCACM0RD is an array of transfer descriptors for Rx (OUT) transfers,
|
// descCDCACM0RD is an array of transfer descriptors for Rx (OUT) transfers,
|
||||||
// which describe to the device controller the location and quantity of data
|
// 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
|
// being received for a given transfer, for the default CDC-ACM (single) device
|
||||||
// class configuration (index 1).
|
// class configuration (index 1).
|
||||||
//go:align 32
|
//go:align 32
|
||||||
var descCDCACM0RD [descCDCACMRDCount]dcdTransfer
|
var descCDCACM0RD [descCDCACMRDCount]dhwTransfer
|
||||||
|
|
||||||
// descCDCACM0TD is an array of transfer descriptors for Tx (IN) transfers,
|
// descCDCACM0TD is an array of transfer descriptors for Tx (IN) transfers,
|
||||||
// which describe to the device controller the location and quantity of data
|
// which describe to the device controller the location and quantity of data
|
||||||
// being transmitted for a given transfer, for the default CDC-ACM (single)
|
// being transmitted for a given transfer, for the default CDC-ACM (single)
|
||||||
// device class configuration (index 1).
|
// device class configuration (index 1).
|
||||||
//go:align 32
|
//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
|
// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of
|
||||||
// the default CDC-ACM (single) device class configuration (index 1).
|
// the default CDC-ACM (single) device class configuration (index 1).
|
||||||
@@ -120,21 +126,22 @@ var descCDCACM0RDNum [descCDCACMRDCount]uint16
|
|||||||
var descCDCACM0RDIdx [descCDCACMRDCount]uint16
|
var descCDCACM0RDIdx [descCDCACMRDCount]uint16
|
||||||
var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16
|
var descCDCACM0RDQue [descCDCACMRDCount + 1]uint16
|
||||||
|
|
||||||
type descCDCACMClass struct {
|
// descCDCACM holds the buffers and control states for all of the CDC-ACM
|
||||||
locale *[descCDCACMLanguageCount]descStringLanguage // string descriptors
|
// (single) device class configurations, ordered by index (offset by -1), for
|
||||||
device *[descLengthDevice]uint8 // device descriptor
|
// iMXRT1062 targets only.
|
||||||
qualif *[descLengthQualification]uint8 // device qualification descriptor
|
//
|
||||||
config *[descCDCACMConfigSize]uint8 // configuration descriptor
|
// 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
|
cd *dhwTransfer // control endpoint 0 Rx/Tx data transfer descriptor
|
||||||
// lineActive int64 // time since last UART DTR/RTS
|
ad *dhwTransfer // control endpoint 0 Rx/Tx ACK transfer descriptor
|
||||||
|
rd *[descCDCACMRDCount]dhwTransfer // bulk data endpoint Rx (OUT) transfer descriptors
|
||||||
qh *[descCDCACMQHCount]dcdEndpoint // endpoint queue heads
|
td *[descCDCACMTDCount]dhwTransfer // bulk data endpoint Tx (IN) transfer descriptors
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
|
cx *[descCDCACMCxCount]uint8 // control endpoint 0 Rx/Tx transfer buffer
|
||||||
rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer
|
rx *[descCDCACMRxCount]uint8 // bulk data endpoint Rx (OUT) transfer buffer
|
||||||
@@ -156,21 +163,18 @@ type descCDCACMClass struct {
|
|||||||
rxCount *[descCDCACMRDCount]uint16
|
rxCount *[descCDCACMRDCount]uint16
|
||||||
rxIndex *[descCDCACMRDCount]uint16
|
rxIndex *[descCDCACMRDCount]uint16
|
||||||
rxQueue *[descCDCACMRDCount + 1]uint16
|
rxQueue *[descCDCACMRDCount + 1]uint16
|
||||||
|
|
||||||
|
_ [2]uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// descCDCACM holds the configuration, endpoint, and transfer descriptors, along
|
// descCDCACMData holds statically-allocated instances for each of the target-
|
||||||
// with the buffers and control states, for all of the CDC-ACM (single) device
|
// specific (iMXRT1062) CDC-ACM (single) device class configurations' control
|
||||||
// class configurations, ordered by configuration index (offset by -1).
|
// and data structures, ordered by configuration index (offset by -1). Each
|
||||||
|
// element is embedded in a corresponding element of descCDCACM.
|
||||||
//go:align 32
|
//go:align 32
|
||||||
var descCDCACM = [descCDCACMCount]descCDCACMClass{
|
var descCDCACMData = [descCDCACMCount]descCDCACMClassData{
|
||||||
{
|
|
||||||
locale: &descCDCACM0String,
|
|
||||||
device: &descCDCACM0Device,
|
|
||||||
qualif: &descCDCACM0Qualif,
|
|
||||||
config: &descCDCACM0Config,
|
|
||||||
|
|
||||||
lineCoding: &descCDCACM0LineCoding,
|
|
||||||
|
|
||||||
|
{ // CDC-ACM (single) class configuration index 1 data
|
||||||
qh: &descCDCACM0QH,
|
qh: &descCDCACM0QH,
|
||||||
|
|
||||||
cd: &descCDCACM0CD,
|
cd: &descCDCACM0CD,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
+50
-7
@@ -1,10 +1,53 @@
|
|||||||
package usb
|
package usb
|
||||||
|
|
||||||
type hcd interface {
|
// Implementation of 32-bit target-agnostic USB host controller driver (hcd).
|
||||||
class() class
|
|
||||||
init() status
|
// hcdCount defines the number of USB cores to configure for host mode. It is
|
||||||
enable(enable bool) status
|
// computed as the sum of all declared host configuration descriptors.
|
||||||
critical(enter bool) status
|
const hcdCount = 0 // + ...
|
||||||
interrupt()
|
|
||||||
udelay(micros uint32)
|
// 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.
|
// Buffered returns the number of bytes currently stored in the RX buffer.
|
||||||
func (uart UART) Buffered() int {
|
func (uart UART) Buffered() int {
|
||||||
dc, ok := uart.core.dc.(*deviceController)
|
return uart.core.dc.uartAvailable()
|
||||||
if !ok {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return dc.uartAvailable()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadByte reads a single byte from the RX buffer.
|
// ReadByte reads a single byte from the RX buffer.
|
||||||
// If there is no data in the buffer, returns an error.
|
// If there is no data in the buffer, returns an error.
|
||||||
func (uart UART) ReadByte() (byte, error) {
|
func (uart UART) ReadByte() (byte, error) {
|
||||||
dc, ok := uart.core.dc.(*deviceController)
|
n, ok := uart.core.dc.uartReadByte()
|
||||||
if !ok {
|
|
||||||
return 0, ErrUARTInvalidCore
|
|
||||||
}
|
|
||||||
n, ok := dc.uartReadByte()
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, ErrUARTEmptyBuffer
|
return 0, ErrUARTEmptyBuffer
|
||||||
}
|
}
|
||||||
@@ -64,20 +56,12 @@ func (uart UART) ReadByte() (byte, error) {
|
|||||||
|
|
||||||
// Read from the RX buffer.
|
// Read from the RX buffer.
|
||||||
func (uart UART) Read(data []byte) (n int, err error) {
|
func (uart UART) Read(data []byte) (n int, err error) {
|
||||||
dc, ok := uart.core.dc.(*deviceController)
|
return uart.core.dc.uartRead(data), nil
|
||||||
if !ok {
|
|
||||||
return 0, ErrUARTInvalidCore
|
|
||||||
}
|
|
||||||
return dc.uartRead(data), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a single byte of data to the UART interface.
|
// WriteByte writes a single byte of data to the UART interface.
|
||||||
func (uart UART) WriteByte(c byte) error {
|
func (uart UART) WriteByte(c byte) error {
|
||||||
dc, ok := uart.core.dc.(*deviceController)
|
if !uart.core.dc.uartWriteByte(c) {
|
||||||
if !ok {
|
|
||||||
return ErrUARTInvalidCore
|
|
||||||
}
|
|
||||||
if !dc.uartWriteByte(c) {
|
|
||||||
return ErrUARTWriteFailed
|
return ErrUARTWriteFailed
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -85,9 +69,5 @@ func (uart UART) WriteByte(c byte) error {
|
|||||||
|
|
||||||
// Write data to the UART.
|
// Write data to the UART.
|
||||||
func (uart UART) Write(data []byte) (n int, err error) {
|
func (uart UART) Write(data []byte) (n int, err error) {
|
||||||
dc, ok := uart.core.dc.(*deviceController)
|
return uart.core.dc.uartWrite(data), nil
|
||||||
if !ok {
|
|
||||||
return 0, ErrUARTInvalidCore
|
|
||||||
}
|
|
||||||
return dc.uartWrite(data), nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
+64
-79
@@ -2,59 +2,6 @@ package usb
|
|||||||
|
|
||||||
// Hardware abstraction for USB ports configured as either host or device.
|
// 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
|
// CoreCount defines the total number of USB cores to configure in device or
|
||||||
// host mode.
|
// host mode.
|
||||||
const CoreCount = dcdCount + hcdCount
|
const CoreCount = dcdCount + hcdCount
|
||||||
@@ -63,26 +10,29 @@ const CoreCount = dcdCount + hcdCount
|
|||||||
// configured on this platform.
|
// configured on this platform.
|
||||||
var coreInstance [CoreCount]core
|
var coreInstance [CoreCount]core
|
||||||
|
|
||||||
// status represents the return code of a subroutine.
|
// core represents the core of a USB port configured as either host or device.
|
||||||
type status uint8
|
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 (
|
const (
|
||||||
statusOK status = iota // Success
|
modeIdle = 0 // USB port has not been configured
|
||||||
statusBusy // Busy
|
modeDevice = 1
|
||||||
statusRetry // Retry
|
modeHost = 2
|
||||||
statusInvalid // Invalid argument
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
// 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
|
// port at given index, if available. Returns a reference to the initialized
|
||||||
// core or nil if the core is unavailable.
|
// core or nil if the core is unavailable.
|
||||||
func initCore(port int, class class) (*core, status) {
|
func initCore(port int, class class) (*core, status) {
|
||||||
|
|
||||||
|
iv := disableInterrupts()
|
||||||
|
defer enableInterrupts(iv)
|
||||||
|
|
||||||
if port < 0 || port >= CoreCount || 0 == class.config {
|
if port < 0 || port >= CoreCount || 0 == class.config {
|
||||||
return nil, statusInvalid
|
return nil, statusInvalid
|
||||||
}
|
}
|
||||||
@@ -90,9 +40,10 @@ func initCore(port int, class class) (*core, status) {
|
|||||||
if modeIdle != coreInstance[port].mode {
|
if modeIdle != coreInstance[port].mode {
|
||||||
// Check if requested port is already configured as requested class. If so,
|
// Check if requested port is already configured as requested class. If so,
|
||||||
// just return a reference to the existing core instead of an error.
|
// 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
|
// This will allow, for instance, TinyGo examples that try to reconfigure
|
||||||
// continue without error.
|
// the USB (CDC-ACM) UART port (which is already configured by the runtime)
|
||||||
|
// to continue without error.
|
||||||
if coreInstance[port].mode == class.mode() {
|
if coreInstance[port].mode == class.mode() {
|
||||||
switch class.mode() {
|
switch class.mode() {
|
||||||
case modeDevice:
|
case modeDevice:
|
||||||
@@ -122,12 +73,7 @@ func initCore(port int, class class) (*core, status) {
|
|||||||
coreInstance[port].port = port
|
coreInstance[port].port = port
|
||||||
coreInstance[port].mode = modeDevice
|
coreInstance[port].mode = modeDevice
|
||||||
coreInstance[port].dc = dc
|
coreInstance[port].dc = dc
|
||||||
// Enable interrupts and enter runtime
|
dc.enable(true) // Enable interrupts and enter runtime
|
||||||
if st = dc.enable(true); !st.ok() {
|
|
||||||
coreInstance[port].mode = modeIdle
|
|
||||||
coreInstance[port].dc = nil
|
|
||||||
return nil, st
|
|
||||||
}
|
|
||||||
|
|
||||||
case modeHost:
|
case modeHost:
|
||||||
// Allocate a free host controller and install interrupts
|
// 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].port = port
|
||||||
coreInstance[port].mode = modeHost
|
coreInstance[port].mode = modeHost
|
||||||
coreInstance[port].hc = hc
|
coreInstance[port].hc = hc
|
||||||
// Enable interrupts and enter runtime
|
hc.enable(true) // Enable interrupts and enter runtime
|
||||||
if st = hc.enable(true); !st.ok() {
|
|
||||||
coreInstance[port].mode = modeIdle
|
|
||||||
coreInstance[port].hc = nil
|
|
||||||
return nil, st
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, statusInvalid
|
return nil, statusInvalid
|
||||||
@@ -155,3 +96,47 @@ func initCore(port int, class class) (*core, status) {
|
|||||||
|
|
||||||
return &coreInstance[port], statusOK
|
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"
|
import "device/arm"
|
||||||
|
|
||||||
|
//go:linkname ticks runtime.ticks
|
||||||
|
func ticks() int64
|
||||||
|
|
||||||
// udelay waits for the given number of microseconds before returning.
|
// udelay waits for the given number of microseconds before returning.
|
||||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
// 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.
|
// an approximate method to spin CPU cycles for short periods of time.
|
||||||
@@ -14,3 +17,11 @@ func udelay(microsec uint32) {
|
|||||||
arm.Asm(`nop`)
|
arm.Asm(`nop`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func disableInterrupts() uintptr {
|
||||||
|
return arm.DisableInterrupts()
|
||||||
|
}
|
||||||
|
|
||||||
|
func enableInterrupts(mask uintptr) {
|
||||||
|
arm.EnableInterrupts(mask)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user