mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 02:57:46 +00:00
rename package machine/usb2 to machine/usb
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
package machine
|
||||
|
||||
import (
|
||||
"machine/usb2"
|
||||
"machine/usb"
|
||||
)
|
||||
|
||||
// USBCDC is the legacy TinyGo type used to implement USB CDC-ACM device class
|
||||
@@ -13,13 +13,13 @@ import (
|
||||
// be removed and usb.UART should be used directly instead.
|
||||
type USBCDC struct {
|
||||
port uint8
|
||||
uart usb2.UART
|
||||
uart usb.UART
|
||||
}
|
||||
|
||||
// Configure the embedded usb.UART with our receiver's settings and the given
|
||||
// UART configuration. This provides compatibility with machine.UART.
|
||||
func (cdc *USBCDC) Configure(config UARTConfig) {
|
||||
cdc.uart.Configure(usb2.UARTConfig{BaudRate: config.BaudRate})
|
||||
cdc.uart.Configure(usb.UARTConfig{BaudRate: config.BaudRate})
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes currently stored in the RX buffer.
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package usb
|
||||
|
||||
// The following constants must be defined for USB 2.0 host/device support.
|
||||
//
|
||||
// However, some or all of these constants may be unused in the core driver
|
||||
// code, depending on which components are allocated by the USB driver.
|
||||
//
|
||||
// These values are __PLATFORM-AGNOSTIC__, and they serve two primary roles:
|
||||
// 1. Aggregate and export values derived from platform-specific constants.
|
||||
// 2. Configure core attributes defined by the USB 2.0 specification.
|
||||
//
|
||||
const (
|
||||
// ConfigPortCount defines the number of USB cores supported on this platform.
|
||||
ConfigPortCount = configDeviceCount + configHostCount
|
||||
)
|
||||
|
||||
// ConfigDeviceDescriptor defines the fields used to populate host-enumerated
|
||||
// device descriptors.
|
||||
type ConfigDeviceDescriptor struct {
|
||||
Manufacturer string
|
||||
Product string
|
||||
VID uint16
|
||||
PID uint16
|
||||
BCD uint16
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb
|
||||
|
||||
const configCPUFrequencyHz = 600000000
|
||||
|
||||
// The following constants must be defined for USB 2.0 host/device support.
|
||||
//
|
||||
// However, some or all of these constants may be unused in the core driver
|
||||
// code, depending on which components are allocated by the USB driver.
|
||||
//
|
||||
// These values are __PLATFORM-SPECIFIC__, and they serve two primary roles:
|
||||
// 1. Determine which components to allocate from the USB driver.
|
||||
// 2. Configure driver attributes NOT defined by the USB 2.0 specification.
|
||||
//
|
||||
const (
|
||||
// configDeviceCount defines the number of USB device-mode ports available.
|
||||
configDeviceCount = configDeviceCDCACMCount
|
||||
|
||||
// configHostCount defines the number of USB host-mode ports available.
|
||||
configHostCount = 0
|
||||
|
||||
// configInterruptQueueSize defines the number of interrupts to retain in the
|
||||
// queue of runtime processes.
|
||||
configInterruptQueueSize = 8
|
||||
|
||||
// configDeviceBufferSize defines the size of the send and receive data
|
||||
// endpoint buffers.
|
||||
configDeviceBufferSize = 512
|
||||
|
||||
// configDeviceMaxEndpoints defines the maximum number of endpoints supported.
|
||||
configDeviceMaxEndpoints = 4
|
||||
|
||||
// configDeviceControllerMaxPacketSize defines the maximum packet size for
|
||||
// communication with an endpoint. The maximum per USB 2.0 spec is 64 bytes,
|
||||
// although a platform may restrict this to something lower if needed.
|
||||
configDeviceControllerMaxPacketSize = 64
|
||||
|
||||
// configDeviceMaxPower defines the maximum power consumption of the USB
|
||||
// device when fully-operational, expressed in 2 mA units.
|
||||
configDeviceMaxPower = 50 // 100 mA
|
||||
|
||||
// configDeviceSelfPowered defines whether the device is self-powered (1) or
|
||||
// not (0).
|
||||
configDeviceSelfPowered = 1
|
||||
|
||||
// configDeviceRemoteWakeup defines whether the device supports remote-wakeup
|
||||
// (1) or not (0).
|
||||
configDeviceRemoteWakeup = 0 // (NOT YET SUPPORTED)
|
||||
|
||||
// configDeviceControllerMaxDTD defines the maximum number of DTD supported.
|
||||
configDeviceControllerMaxDTD = 16
|
||||
|
||||
// configDeviceControllerQHAlign defines the memory alignment of QH buffer.
|
||||
// Ensure this agrees with the go:align pragma on deviceControllerQHBuffer.
|
||||
configDeviceControllerQHAlign = 2048
|
||||
|
||||
// configDeviceControllerDTDAlign defines the memory alignment of DTD buffer.
|
||||
// Ensure this agrees with the go:align pragma on deviceControllerDTDBuffer.
|
||||
configDeviceControllerDTDAlign = 32
|
||||
)
|
||||
|
||||
// If USB CDC-ACM device support is required, the following constants must be
|
||||
// defined.
|
||||
const (
|
||||
// configDeviceCDCACMCount defines the number of USB CDC-ACM interfaces
|
||||
// to initialize; must be less-than or equal to configDeviceCount.
|
||||
configDeviceCDCACMCount = 1
|
||||
|
||||
// configDeviceCDCACMConfigurationCount defines the number of configurations
|
||||
// required for each CDC-ACM device port.
|
||||
configDeviceCDCACMConfigurationCount = 1
|
||||
|
||||
// configDeviceCDCACMConfigurationIndex defines the default configuration
|
||||
// index used to initialize CDC-ACM device class configurations. This is a
|
||||
// 1-based index into the list of CDC-ACM configurations (which are 0-based
|
||||
// slices, and thus this index is offset by -1). A configuration index of 0
|
||||
// represents the invalid configuration index.
|
||||
configDeviceCDCACMConfigurationIndex = 1
|
||||
|
||||
// configDeviceCDCACMInterfaceCount defines the number of interfaces required
|
||||
// for each CDC-ACM configuration.
|
||||
configDeviceCDCACMInterfaceCount = 2
|
||||
|
||||
// configDeviceCDCACMInterruptInPacketSize defines the packet size of CDC-ACM
|
||||
// communication interface's interrupt input endpoint.
|
||||
configDeviceCDCACMInterruptInPacketSize = configDeviceCDCACMFSInterruptInPacketSize // (full-speed)
|
||||
|
||||
// configDeviceCDCACMBulkInPacketSize defines the packet size of CDC-ACM data
|
||||
// interface's bulk input endpoint.
|
||||
configDeviceCDCACMBulkInPacketSize = configDeviceCDCACMFSBulkInPacketSize // (full-speed)
|
||||
|
||||
// configDeviceCDCACMBulkOutPacketSize defines the packet size of CDC-ACM data
|
||||
// interface's bulk output endpoint.
|
||||
configDeviceCDCACMBulkOutPacketSize = configDeviceCDCACMFSBulkOutPacketSize // (full-speed)
|
||||
|
||||
// configDeviceCDCACMInterruptInInterval defines the interval of CDC-ACM
|
||||
// communication interface's interrupt input endpoint.
|
||||
configDeviceCDCACMInterruptInInterval = configDeviceCDCACMFSInterruptInInterval // (full-speed)
|
||||
)
|
||||
|
||||
// If USB CDC-ACM device support is required, the following arrays must be
|
||||
// initialized with each device's CDC-ACM configuration.
|
||||
var (
|
||||
// configDeviceCDCACM defines the configuration specific to CDC-ACM devices
|
||||
// including the properties of endpoints required by the device class driver,
|
||||
// as well as the serial UART line coding properties.
|
||||
configDeviceCDCACM = [configDeviceCDCACMCount][configDeviceCDCACMConfigurationCount]deviceCDCACMConfig{
|
||||
{{ // USB CDC-ACM [0]
|
||||
interfaceSpeed: specSpeedFull, // USB full-speed (12 Mbit/s)
|
||||
// Serial line configuration
|
||||
lineCodingSize: 7, // Size of line-coding message
|
||||
lineCodingBaudRate: 115200, // Data terminal rate
|
||||
lineCodingCharFormat: 0, // Character format
|
||||
lineCodingParityType: 0, // Parity type
|
||||
lineCodingDataBits: 8, // Data word size
|
||||
// Communication/control interface
|
||||
commInterfaceIndex: descriptorInterfaceCDCACMComm, // communication/control interface index
|
||||
commInterruptInEndpoint: descriptorEndpointCDCACMCommInterruptIn, // interrupt input endpoint index (address)
|
||||
commInterruptInPacketSize: configDeviceCDCACMInterruptInPacketSize,
|
||||
commInterruptInInterval: configDeviceCDCACMInterruptInInterval,
|
||||
// Data interface
|
||||
dataInterfaceIndex: descriptorInterfaceCDCACMData, // data interface index
|
||||
dataBulkInEndpoint: descriptorEndpointCDCACMDataBulkIn, // bulk input endpoint index (address)
|
||||
dataBulkInPacketSize: configDeviceCDCACMBulkInPacketSize,
|
||||
dataBulkOutEndpoint: descriptorEndpointCDCACMDataBulkOut, // bulk output endpoint index (address)
|
||||
dataBulkOutPacketSize: configDeviceCDCACMBulkOutPacketSize,
|
||||
}},
|
||||
}
|
||||
|
||||
// configDeviceCDCACMDescriptor defines the generic USB 2.0 descriptors used
|
||||
// to describe each CDC-ACM device enabled.
|
||||
//
|
||||
// Note that the actual descriptor byte slices need not (should not) be
|
||||
// defined here. Instead, a reusable global slice should be declared as a
|
||||
// standalone type and not a member of a composite type; this allows both
|
||||
// reusability and better control over the memory layout/alignment for USB
|
||||
// PHYs that may require constraints of this sort. The fields of struct
|
||||
// deviceDescriptor are therefore all pointers to byte slices so that they
|
||||
// can be initialized with references to the aforementioned global arrays.
|
||||
configDeviceCDCACMDescriptor = [configDeviceCDCACMCount]deviceDescriptor{
|
||||
{ // USB CDC-ACM [0]
|
||||
pDevice: &descriptorDeviceCDCACM,
|
||||
pConfig: &descriptorConfigurationCDCACM,
|
||||
language: []deviceDescriptorLanguage{
|
||||
{
|
||||
pString: []deviceDescriptorString{
|
||||
&descriptorStringCDCACMLanguage,
|
||||
&configDescriptorStringCDCACMManufacturer,
|
||||
&configDescriptorStringCDCACMProduct,
|
||||
},
|
||||
ident: 0x0409,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// default device descriptor info (little-endian byte order).
|
||||
configDescriptorDeviceCDCACMVID = []uint8{0xC9, 0x1F} // USB Vendor ID (0x1FC9)
|
||||
configDescriptorDeviceCDCACMPID = []uint8{0x94, 0x00} // USB Product ID (0x0094)
|
||||
configDescriptorDeviceCDCACMBCD = []uint8{0x01, 0x01} // USB Device Version (0x0101)
|
||||
|
||||
configDescriptorStringCDCACMManufacturer = []uint8{
|
||||
2 + 2*18, specDescriptorTypeString,
|
||||
'N', 0,
|
||||
'X', 0,
|
||||
'P', 0,
|
||||
' ', 0,
|
||||
'S', 0,
|
||||
'e', 0,
|
||||
'm', 0,
|
||||
'i', 0,
|
||||
'c', 0,
|
||||
'o', 0,
|
||||
'n', 0,
|
||||
'd', 0,
|
||||
'u', 0,
|
||||
'c', 0,
|
||||
't', 0,
|
||||
'o', 0,
|
||||
'r', 0,
|
||||
's', 0,
|
||||
}
|
||||
|
||||
configDescriptorStringCDCACMProduct = []uint8{
|
||||
2 + 2*20, specDescriptorTypeString,
|
||||
'T', 0,
|
||||
'i', 0,
|
||||
'n', 0,
|
||||
'y', 0,
|
||||
'G', 0,
|
||||
'o', 0,
|
||||
' ', 0,
|
||||
'U', 0,
|
||||
'S', 0,
|
||||
'B', 0,
|
||||
' ', 0,
|
||||
'(', 0,
|
||||
'C', 0,
|
||||
'D', 0,
|
||||
'C', 0,
|
||||
'-', 0,
|
||||
'A', 0,
|
||||
'C', 0,
|
||||
'M', 0,
|
||||
')', 0,
|
||||
}
|
||||
)
|
||||
|
||||
// The following additional constants are not required by the USB driver but are
|
||||
// used by the usb package on this platform.
|
||||
const (
|
||||
|
||||
// configInterruptPriority defines the priority number for USB interrupts.
|
||||
configInterruptPriority = 3
|
||||
|
||||
// configDeviceControllerMaxPrimeAttempts defines the maximum number of
|
||||
// attempts to prime an endpoint for transfer. If attempts exceeds this
|
||||
// value, then the endpoint status has been reset.
|
||||
configDeviceControllerMaxPrimeAttempts = 10000000
|
||||
|
||||
// USB CDC-ACM high-speed (480 Mbit/s) packet size
|
||||
configDeviceCDCACMHSInterruptInPacketSize = 16
|
||||
configDeviceCDCACMHSInterruptInInterval = 7 // 2^(7-1)/8 = 8ms
|
||||
configDeviceCDCACMHSBulkInPacketSize = 512
|
||||
configDeviceCDCACMHSBulkOutPacketSize = 512
|
||||
|
||||
// USB CDC-ACM full-speed (12 Mbit/s) packet size
|
||||
configDeviceCDCACMFSInterruptInPacketSize = 16
|
||||
configDeviceCDCACMFSInterruptInInterval = 8 // 2^(8-1)/8 = 16ms
|
||||
configDeviceCDCACMFSBulkInPacketSize = 64
|
||||
configDeviceCDCACMFSBulkOutPacketSize = 64
|
||||
)
|
||||
@@ -1,4 +1,4 @@
|
||||
package usb2
|
||||
package usb
|
||||
|
||||
import "unsafe"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb2
|
||||
package usb
|
||||
|
||||
// Implementation of USB device controller interface (dcd) for NXP iMXRT1062.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package usb2
|
||||
package usb
|
||||
|
||||
const descUSBSpecVersion = uint16(0x0200) // USB 2.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb2
|
||||
package usb
|
||||
|
||||
// descCPUFrequencyHz defines the target CPU frequency (Hz).
|
||||
const descCPUFrequencyHz = 600000000
|
||||
@@ -1,162 +0,0 @@
|
||||
package usb
|
||||
|
||||
// USB specification version number (BCD)
|
||||
var descriptorUSBSpecification = []uint8{0x00, 0x02} // USB 2.0
|
||||
|
||||
// USB CDC-ACM descriptor constants
|
||||
const (
|
||||
// interface indices
|
||||
descriptorInterfaceCDCACMComm = 0 // communication/control interface
|
||||
descriptorInterfaceCDCACMData = 1 // data interface
|
||||
|
||||
descriptorEndpointCDCACMCommInterruptIn = 1 // interrupt endpoint (input)
|
||||
descriptorEndpointCDCACMDataBulkIn = 2 // data endpoint (input)
|
||||
descriptorEndpointCDCACMDataBulkOut = 3 // data endpoint (output)
|
||||
|
||||
descriptorConfigurationCDCACMHeaderFuncSize = 5
|
||||
descriptorConfigurationCDCACMCallManageSize = 5
|
||||
descriptorConfigurationCDCACMAbstractSize = 4
|
||||
descriptorConfigurationCDCACMUnionFuncSize = 5
|
||||
|
||||
descriptorConfigurationCDCACMSize = uint16(
|
||||
specDescriptorLengthConfigure + // configuration
|
||||
specDescriptorLengthInterface + // communication/control interface
|
||||
descriptorConfigurationCDCACMHeaderFuncSize + // CDC header
|
||||
descriptorConfigurationCDCACMCallManageSize + // CDC call management
|
||||
descriptorConfigurationCDCACMAbstractSize + // CDC abstract control
|
||||
descriptorConfigurationCDCACMUnionFuncSize + // CDC union
|
||||
specDescriptorLengthEndpoint + // communication/control input endpoint
|
||||
specDescriptorLengthInterface + // data interface
|
||||
specDescriptorLengthEndpoint + // data input endpoint
|
||||
specDescriptorLengthEndpoint) // data output endpoint
|
||||
|
||||
descriptorConfigurationCDCACMAttributes = (specDescriptorConfigureAttributeD7Msk) | // Bit 7: reserved (1)
|
||||
(configDeviceSelfPowered << specDescriptorConfigureAttributeSelfPoweredPos) | // Bit 6: self-powered
|
||||
(configDeviceRemoteWakeup << specDescriptorConfigureAttributeRemoteWakeupPos) | // Bit 5: remote wakeup
|
||||
0 // Bits 0-4: reserved (0)
|
||||
)
|
||||
|
||||
// USB 2.0 CDC-ACM descriptors
|
||||
var (
|
||||
// descriptorDeviceCDCACM is the default device descriptor for CDC-ACM
|
||||
// devices.
|
||||
descriptorDeviceCDCACM = []uint8{
|
||||
specDescriptorLengthDevice, // Size of this descriptor in bytes
|
||||
specDescriptorTypeDevice, // DEVICE Descriptor Type
|
||||
descriptorUSBSpecification[0], // USB Specification Release Number in BCD (low)
|
||||
descriptorUSBSpecification[1], // USB Specification Release Number in BCD (high)
|
||||
uint8(deviceClassCDC), // Class code (assigned by the USB-IF).
|
||||
0, // Subclass code (assigned by the USB-IF).
|
||||
deviceCDCNoClassSpecificProtocol, // Protocol code (assigned by the USB-IF).
|
||||
configDeviceControllerMaxPacketSize, // Maximum packet size for endpoint zero (8, 16, 32, or 64)
|
||||
configDescriptorDeviceCDCACMVID[0], // Vendor ID (low) (assigned by the USB-IF)
|
||||
configDescriptorDeviceCDCACMVID[1], // Vendor ID (high) (assigned by the USB-IF)
|
||||
configDescriptorDeviceCDCACMPID[0], // Product ID (low) (assigned by the manufacturer)
|
||||
configDescriptorDeviceCDCACMPID[1], // Product ID (high) (assigned by the manufacturer)
|
||||
configDescriptorDeviceCDCACMBCD[0], // Device release number in BCD (low)
|
||||
configDescriptorDeviceCDCACMBCD[1], // Device release number in BCD (high)
|
||||
0x01, // Index of string descriptor describing manufacturer
|
||||
0x02, // Index of string descriptor describing product
|
||||
0x00, // Index of string descriptor describing the device's serial number
|
||||
configDeviceCDCACMConfigurationCount, // Number of possible configurations
|
||||
}
|
||||
|
||||
// descriptorConfigurationCDCACM is the default configuration descriptor for
|
||||
// CDC-ACM devices.
|
||||
descriptorConfigurationCDCACM = []uint8{
|
||||
specDescriptorLengthConfigure, // Size of this descriptor in bytes
|
||||
specDescriptorTypeConfigure, // Descriptor Type
|
||||
uint8(descriptorConfigurationCDCACMSize), // Total length of data returned for this configuration (low)
|
||||
uint8(descriptorConfigurationCDCACMSize >> 8), // Total length of data returned for this configuration (high)
|
||||
configDeviceCDCACMInterfaceCount, // Number of interfaces supported by this configuration
|
||||
configDeviceCDCACMConfigurationIndex, // Value to use to select this configuration
|
||||
0, // Index of string descriptor describing this configuration
|
||||
descriptorConfigurationCDCACMAttributes, // Configuration attributes
|
||||
configDeviceMaxPower, // Max power consumption when fully-operational (2 mA units)
|
||||
|
||||
// Communication/Control Interface Descriptor
|
||||
specDescriptorLengthInterface, // Descriptor length
|
||||
specDescriptorTypeInterface, // Descriptor type
|
||||
descriptorInterfaceCDCACMComm, // Interface index
|
||||
0, // Alternate setting
|
||||
1, // Number of endpoints
|
||||
deviceCDCCommClass, // Class code
|
||||
deviceCDCAbstractControlModel, // Subclass code
|
||||
deviceCDCNoClassSpecificProtocol, // Protocol code (NOTE: Teensyduino defines this as 1 [AT V.250])
|
||||
0, // Interface Description String Index
|
||||
|
||||
// CDC Header Functional Descriptor
|
||||
descriptorConfigurationCDCACMHeaderFuncSize, // Size of this descriptor in bytes
|
||||
specDescriptorTypeCDCInterface, // Descriptor Type
|
||||
deviceCDCHeaderFuncDesc, // Descriptor Subtype
|
||||
0x10, // USB CDC specification version 1.10 (low)
|
||||
0x01, // USB CDC specification version 1.10 (high)
|
||||
|
||||
// CDC Call Management Functional Descriptor
|
||||
descriptorConfigurationCDCACMCallManageSize, // Size of this descriptor in bytes
|
||||
specDescriptorTypeCDCInterface, // Descriptor Type
|
||||
deviceCDCCallManagementFuncDesc, // Descriptor Subtype
|
||||
0x01, // Capabilities
|
||||
1, // Data Interface
|
||||
|
||||
// CDC Abstract Control Management Functional Descriptor
|
||||
descriptorConfigurationCDCACMAbstractSize, // Size of this descriptor in bytes
|
||||
specDescriptorTypeCDCInterface, // Descriptor Type
|
||||
deviceCDCAbstractControlFuncDesc, // Descriptor Subtype
|
||||
0x06, // Capabilities
|
||||
|
||||
// CDC Union Functional Descriptor
|
||||
descriptorConfigurationCDCACMUnionFuncSize, // Size of this descriptor in bytes
|
||||
specDescriptorTypeCDCInterface, // Descriptor Type
|
||||
deviceCDCUnionFuncDesc, // Descriptor Subtype
|
||||
0, // Controlling interface index
|
||||
1, // Controlled interface index
|
||||
|
||||
// Communication/Control Notification Endpoint descriptor
|
||||
specDescriptorLengthEndpoint, // Size of this descriptor in bytes
|
||||
specDescriptorTypeEndpoint, // Descriptor Type
|
||||
descriptorEndpointCDCACMCommInterruptIn | // Endpoint address
|
||||
specDescriptorEndpointAddressDirectionIn,
|
||||
uint8(specEndpointInterrupt), // Attributes
|
||||
uint8(configDeviceCDCACMInterruptInPacketSize), // Max packet size (low)
|
||||
uint8(configDeviceCDCACMInterruptInPacketSize >> 8), // Max packet size (high)
|
||||
configDeviceCDCACMInterruptInInterval, // Polling Interval
|
||||
|
||||
// Data Interface Descriptor
|
||||
specDescriptorLengthInterface, // Interface length
|
||||
specDescriptorTypeInterface, // Interface type
|
||||
descriptorInterfaceCDCACMData, // Interface index
|
||||
0, // Alternate setting
|
||||
2, // Number of endpoints
|
||||
deviceCDCDataClass, // Class code
|
||||
0, // Subclass code
|
||||
deviceCDCNoClassSpecificProtocol, // Protocol code
|
||||
0, // Interface Description String Index
|
||||
|
||||
// Data Bulk Input Endpoint descriptor
|
||||
specDescriptorLengthEndpoint, // Size of this descriptor in bytes
|
||||
specDescriptorTypeEndpoint, // Descriptor Type
|
||||
descriptorEndpointCDCACMDataBulkIn | // Endpoint address
|
||||
specDescriptorEndpointAddressDirectionIn,
|
||||
uint8(specEndpointBulk), // Attributes
|
||||
uint8(configDeviceCDCACMBulkInPacketSize), // Max packet size (low)
|
||||
uint8(configDeviceCDCACMBulkInPacketSize >> 8), // Max packet size (high)
|
||||
0, // Polling Interval
|
||||
|
||||
// Data Bulk Output Endpoint descriptor
|
||||
specDescriptorLengthEndpoint, // Size of this descriptor in bytes
|
||||
specDescriptorTypeEndpoint, // Descriptor Type
|
||||
descriptorEndpointCDCACMDataBulkOut | // Endpoint address
|
||||
specDescriptorEndpointAddressDirectionOut,
|
||||
uint8(specEndpointBulk), // Attributes
|
||||
uint8(configDeviceCDCACMBulkOutPacketSize), // Max packet size (low)
|
||||
uint8(configDeviceCDCACMBulkOutPacketSize >> 8), // Max packet size (high)
|
||||
0, // Polling Interval
|
||||
}
|
||||
|
||||
descriptorStringCDCACMLanguage = []uint8{
|
||||
2 + 2*1, // Size of this descriptor in bytes
|
||||
specDescriptorTypeString,
|
||||
0x09, 0x04,
|
||||
}
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,135 +0,0 @@
|
||||
package usb
|
||||
|
||||
const (
|
||||
// Communication Class
|
||||
deviceCDCCommClass = 0x02
|
||||
// Data Class
|
||||
deviceCDCDataClass = 0x0A
|
||||
|
||||
// Communication Class SubClass Codes
|
||||
deviceCDCDirectLineControlModel = 0x01
|
||||
deviceCDCAbstractControlModel = 0x02
|
||||
deviceCDCTelephoneControlModel = 0x03
|
||||
deviceCDCMultiChannelControlModel = 0x04
|
||||
deviceCDCCAPIControlMopdel = 0x05
|
||||
deviceCDCEthernetNetworkingControlModel = 0x06
|
||||
deviceCDCATMNetworkingControlModel = 0x07
|
||||
deviceCDCWirelessHandsetControlModel = 0x08
|
||||
deviceCDCDeviceManagement = 0x09
|
||||
deviceCDCMobileDirectLineModel = 0x0A
|
||||
deviceCDCOBEX = 0x0B
|
||||
deviceCDCEthernetEmulationModel = 0x0C
|
||||
|
||||
// Communication Class Protocol Codes
|
||||
deviceCDCNoClassSpecificProtocol = 0x00 // also for Data Class Protocol Code
|
||||
deviceCDCAT250Protocol = 0x01
|
||||
deviceCDCATPCCA101Protocol = 0x02
|
||||
deviceCDCATPCCA101AnnexO = 0x03
|
||||
deviceCDCATGSM707 = 0x04
|
||||
deviceCDCAT3GPP27007 = 0x05
|
||||
deviceCDCATTIACDMA = 0x06
|
||||
deviceCDCEthernetEmulationProtocol = 0x07
|
||||
deviceCDCExternalProtocol = 0xFE
|
||||
deviceCDCVendorSpecific = 0xFF // also for Data Class Protocol Code
|
||||
|
||||
// Data Class Protocol Codes
|
||||
deviceCDCPyhsicalInterfaceProtocol = 0x30
|
||||
deviceCDCHDLCProtocol = 0x31
|
||||
deviceCDCTransparentProtocol = 0x32
|
||||
deviceCDCManagementProtocol = 0x50
|
||||
deviceCDCDataLinkQ931Protocol = 0x51
|
||||
deviceCDCDataLinkQ921Protocol = 0x52
|
||||
deviceCDCDataCompressionV42BIS = 0x90
|
||||
deviceCDCEuroISDNProtocol = 0x91
|
||||
deviceCDCRateAdaptionISDNV24 = 0x92
|
||||
deviceCDCCAPICommands = 0x93
|
||||
deviceCDCHostBasedDriver = 0xFD
|
||||
deviceCDCUnitFunctional = 0xFE
|
||||
|
||||
// Descriptor SubType in Communications Class Functional Descriptors
|
||||
deviceCDCHeaderFuncDesc = 0x00
|
||||
deviceCDCCallManagementFuncDesc = 0x01
|
||||
deviceCDCAbstractControlFuncDesc = 0x02
|
||||
deviceCDCDirectLineFuncDesc = 0x03
|
||||
deviceCDCTelephoneRingerFuncDesc = 0x04
|
||||
deviceCDCTelephoneReportFuncDesc = 0x05
|
||||
deviceCDCUnionFuncDesc = 0x06
|
||||
deviceCDCCountrySelectFuncDesc = 0x07
|
||||
deviceCDCTelephoneModesFuncDesc = 0x08
|
||||
deviceCDCTerminalFuncDesc = 0x09
|
||||
deviceCDCNetworkChannelFuncDesc = 0x0A
|
||||
deviceCDCProtocolUnitFuncDesc = 0x0B
|
||||
deviceCDCExtensionUnitFuncDesc = 0x0C
|
||||
deviceCDCMultiChannelFuncDesc = 0x0D
|
||||
deviceCDCCAPIControlFuncDesc = 0x0E
|
||||
deviceCDCEthernetNetworkingFuncDesc = 0x0F
|
||||
deviceCDCATMNetworkingFuncDesc = 0x10
|
||||
deviceCDCWirelessControlFuncDesc = 0x11
|
||||
deviceCDCMobileDirectLineFuncDesc = 0x12
|
||||
deviceCDCMDLMDetailFuncDesc = 0x13
|
||||
deviceCDCDeviceManagementFuncDesc = 0x14
|
||||
deviceCDCOBEXFuncDesc = 0x15
|
||||
deviceCDCCommandSetFuncDesc = 0x16
|
||||
deviceCDCCommandSetDetailFuncDesc = 0x17
|
||||
deviceCDCTelephoneControlFuncDesc = 0x18
|
||||
deviceCDCOBEXServiceIDFuncDesc = 0x19
|
||||
|
||||
deviceCDCRequestSendEncapsulatedCommand = 0x00 // CDC request SEND_ENCAPSULATED_COMMAND
|
||||
deviceCDCRequestGetEncapsulatedResponse = 0x01 // CDC request GET_ENCAPSULATED_RESPONSE
|
||||
deviceCDCRequestSetCommFeature = 0x02 // CDC request SET_COMM_FEATURE
|
||||
deviceCDCRequestGetCommFeature = 0x03 // CDC request GET_COMM_FEATURE
|
||||
deviceCDCRequestClearCommFeature = 0x04 // CDC request CLEAR_COMM_FEATURE
|
||||
deviceCDCRequestSetAuxLineState = 0x10 // CDC request SET_AUX_LINE_STATE
|
||||
deviceCDCRequestSetHookState = 0x11 // CDC request SET_HOOK_STATE
|
||||
deviceCDCRequestPulseSetup = 0x12 // CDC request PULSE_SETUP
|
||||
deviceCDCRequestSendPulse = 0x13 // CDC request SEND_PULSE
|
||||
deviceCDCRequestSetPulseTime = 0x14 // CDC request SET_PULSE_TIME
|
||||
deviceCDCRequestRingAuxJack = 0x15 // CDC request RING_AUX_JACK
|
||||
deviceCDCRequestSetLineCoding = 0x20 // CDC request SET_LINE_CODING
|
||||
deviceCDCRequestGetLineCoding = 0x21 // CDC request GET_LINE_CODING
|
||||
deviceCDCRequestSetControlLineState = 0x22 // CDC request SET_CONTROL_LINE_STATE
|
||||
deviceCDCRequestSendBreak = 0x23 // CDC request SEND_BREAK
|
||||
deviceCDCRequestSetRingerParams = 0x30 // CDC request SET_RINGER_PARAMS
|
||||
deviceCDCRequestGetRingerParams = 0x31 // CDC request GET_RINGER_PARAMS
|
||||
deviceCDCRequestSetOperationParam = 0x32 // CDC request SET_OPERATION_PARAM
|
||||
deviceCDCRequestGetOperationParam = 0x33 // CDC request GET_OPERATION_PARAM
|
||||
deviceCDCRequestSetLineParams = 0x34 // CDC request SET_LINE_PARAMS
|
||||
deviceCDCRequestGetLineParams = 0x35 // CDC request GET_LINE_PARAMS
|
||||
deviceCDCRequestDialDigits = 0x36 // CDC request DIAL_DIGITS
|
||||
deviceCDCRequestSetUnitParameter = 0x37 // CDC request SET_UNIT_PARAMETER
|
||||
deviceCDCRequestGetUnitParameter = 0x38 // CDC request GET_UNIT_PARAMETER
|
||||
deviceCDCRequestClearUnitParameter = 0x39 // CDC request CLEAR_UNIT_PARAMETER
|
||||
deviceCDCRequestSetEthernetMulticastFilters = 0x40 // CDC request SET_ETHERNET_MULTICAST_FILTERS
|
||||
deviceCDCRequestSetEthernetPowPatternFilter = 0x41 // CDC request SET_ETHERNET_POW_PATTER_FILTER
|
||||
deviceCDCRequestGetEthernetPowPatternFilter = 0x42 // CDC request GET_ETHERNET_POW_PATTER_FILTER
|
||||
deviceCDCRequestSetEthernetPacketFilter = 0x43 // CDC request SET_ETHERNET_PACKET_FILTER
|
||||
deviceCDCRequestGetEthernetStatistic = 0x44 // CDC request GET_ETHERNET_STATISTIC
|
||||
deviceCDCRequestSetATMDataFormat = 0x50 // CDC request SET_ATM_DATA_FORMAT
|
||||
deviceCDCRequestGetATMDeviceStatistics = 0x51 // CDC request GET_ATM_DEVICE_STATISTICS
|
||||
deviceCDCRequestSetATMDefaultVC = 0x52 // CDC request SET_ATM_DEFAULT_VC
|
||||
deviceCDCRequestGetATMVCStatistics = 0x53 // CDC request GET_ATM_VC_STATISTICS
|
||||
deviceCDCRequestMDLMSpecificRequestsMask = 0x7F // CDC request MDLM_SPECIFIC_REQUESTS_MASK
|
||||
|
||||
deviceCDCNotifyNetworkConnection = 0x00 // CDC notify NETWORK_CONNECTION
|
||||
deviceCDCNotifyResponseAvail = 0x01 // CDC notify RESPONSE_AVAIL
|
||||
deviceCDCNotifyAuxJackHookState = 0x08 // CDC notify AUX_JACK_HOOK_STATE
|
||||
deviceCDCNotifyRingDetect = 0x09 // CDC notify RING_DETECT
|
||||
deviceCDCNotifySerialState = 0x20 // CDC notify SERIAL_STATE
|
||||
deviceCDCNotifyCallStateChange = 0x28 // CDC notify CALL_STATE_CHANGE
|
||||
deviceCDCNotifyLineStateChange = 0x29 // CDC notify LINE_STATE_CHANGE
|
||||
deviceCDCNotifyConnectionSpeedChange = 0x2A // CDC notify CONNECTION_SPEED_CHANGE
|
||||
|
||||
deviceCDCFeatureAbstractState = 0x01 // CDC feature select ABSTRACT_STATE
|
||||
deviceCDCFeatureCountrySetting = 0x02 // CDC feature select COUNTRY_SETTING
|
||||
|
||||
deviceCDCControlSigBitmapCarrierActivation = 0x02 // CDC control signal CARRIER_ACTIVATION
|
||||
deviceCDCControlSigBitmapDTEPresence = 0x01 // CDC control signal DTE_PRESENCE
|
||||
deviceCDCUARTStateRxCarrier = 0x01 // UART state RX_CARRIER
|
||||
deviceCDCUARTStateTxCarrier = 0x02 // UART state TX_CARRIER
|
||||
deviceCDCUARTStateBreak = 0x04 // UART state BREAK
|
||||
deviceCDCUARTStateRingSignal = 0x08 // UART state RING_SIGNAL
|
||||
deviceCDCUARTStateFraming = 0x10 // UART state FRAMING
|
||||
deviceCDCUARTStateParity = 0x20 // UART state PARITY
|
||||
deviceCDCUARTStateOverrun = 0x40 // UART state OVERRUN
|
||||
|
||||
)
|
||||
@@ -1,690 +0,0 @@
|
||||
package usb
|
||||
|
||||
import "bytes"
|
||||
|
||||
type (
|
||||
deviceCDCACMEventID uint8
|
||||
|
||||
// deviceCDCACMConfig defines the platform-specific configuration settings for
|
||||
// a single CDC-ACM device class on a given USB port.
|
||||
//
|
||||
// To connect the USB CDC-ACM device driver to a particular platform, there
|
||||
// should be one instance for each USB CDC-ACM port, defined in global array
|
||||
// configDeviceCDCACM, indexed by USB port.
|
||||
deviceCDCACMConfig struct {
|
||||
// USB configuration
|
||||
interfaceSpeed uint8 // Low/Full/High-speed
|
||||
// CDC-ACM Serial line configuration
|
||||
lineCodingSize uint32 // Size of line-coding message
|
||||
lineCodingBaudRate uint32 // Data terminal rate
|
||||
lineCodingCharFormat uint32 // Character format
|
||||
lineCodingParityType uint32 // Parity type
|
||||
lineCodingDataBits uint32 // Data word size
|
||||
// CDC-ACM Communication/control interface
|
||||
commInterfaceIndex uint8 // Communication/control interface index
|
||||
commInterruptInEndpoint uint8 // Interrupt input endpoint index (address)
|
||||
commInterruptInPacketSize uint16 // Interrupt input packet size
|
||||
commInterruptInInterval uint8 // Interrupt input interval
|
||||
// CDC-ACM Data interface
|
||||
dataInterfaceIndex uint8 // Data interface index
|
||||
dataBulkInEndpoint uint8 // Bulk input endpoint index (address)
|
||||
dataBulkInPacketSize uint16 // Bulk input packet size
|
||||
dataBulkOutEndpoint uint8 // Bulk output endpoint index (address)
|
||||
dataBulkOutPacketSize uint16 // Bulk output packet size
|
||||
}
|
||||
|
||||
deviceCDCACM struct {
|
||||
device *device // The handle of the USB device.
|
||||
config *deviceClassConfig // The class configure structure.
|
||||
info deviceCDCACMInfo // The ACM serial state.
|
||||
comm *deviceInterface // The CDC communication interface handle.
|
||||
data *deviceInterface // The CDC data interface handle.
|
||||
sendBuffer *deviceCDCACMDataBuffer // Pointer to the global send buffer
|
||||
recvBuffer *deviceCDCACMDataBuffer // Pointer to the global receive buffer
|
||||
bulkIn deviceCDCACMPipe // The bulk in pipe for sending packet to host.
|
||||
bulkOut deviceCDCACMPipe // The bulk out pipe for receiving packet from host.
|
||||
interruptIn deviceCDCACMPipe // The interrupt in pipe for notifying the device state to host.
|
||||
interfaceNumber uint8 // The current interface number.
|
||||
alternate uint8 // The alternate setting value of the interface.
|
||||
hasSentState bool // The device has primed the state in interrupt pipe
|
||||
speed uint8 // Speed of USB device (Full/Low/High)
|
||||
lineCodingSize uint32 // Size of line-coding message
|
||||
baudRate uint32 // Data terminal rate
|
||||
charFormat uint32 // Character format
|
||||
parityType uint32 // Parity type
|
||||
dataBits uint32 // Data word size
|
||||
sendSize uint32 // Number of bytes scheduled in global send buffer
|
||||
recvSize uint32 // Number of bytes scheduled in global receive buffer
|
||||
}
|
||||
|
||||
deviceCDCACMDataBuffer [configDeviceBufferSize]uint8
|
||||
deviceCDCACMAlternateList [configDeviceCDCACMInterfaceCount]uint16
|
||||
deviceCDCACMSerialState [deviceCDCACMSerialStateSize]uint8
|
||||
|
||||
deviceCDCACMRequestParam struct {
|
||||
buffer *[]uint8 // The pointer to the address of the buffer for CDC class request.
|
||||
length *uint32 // The pointer to the length of the buffer for CDC class request.
|
||||
interfaceIndex uint16 // The interface index of the setup packet.
|
||||
setupValue uint16 // The wValue field of the setup packet.
|
||||
isSetup bool // The flag indicates if it is a setup packet
|
||||
}
|
||||
|
||||
deviceCDCACMPipe struct {
|
||||
pipeDataBuffer []uint8 // pipe data buffer backup when stall
|
||||
pipeDataLen uint32 // pipe data length backup when stall
|
||||
pipeStall bool // pipe is stall
|
||||
ep uint8 // The endpoint number of the pipe.
|
||||
isBusy bool // The pipe is transferring packet
|
||||
}
|
||||
|
||||
deviceCDCACMInfo struct {
|
||||
serialState deviceCDCACMSerialState // Serial state buffer of the CDC device to notify the serial state to host.
|
||||
dtePresent bool // A flag to indicate whether DTE is present.
|
||||
breakDuration uint16 // Length of time in milliseconds of the break signal
|
||||
dteStatus uint8 // Status of data terminal equipment
|
||||
currentInterface uint8 // Current interface index.
|
||||
uartState uint16 // UART state of the CDC device.
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
deviceCDCACMEventSendResponse deviceCDCACMEventID = iota + 1 // This event indicates the bulk send transfer is complete or cancelled etc.
|
||||
deviceCDCACMEventRecvResponse // This event indicates the bulk receive transfer is complete or cancelled etc..
|
||||
deviceCDCACMEventSerialStateNotify // This event indicates the serial state has been sent to the host.
|
||||
deviceCDCACMEventSendEncapsulatedCommand // This event indicates the device received the SEND_ENCAPSULATED_COMMAND request.
|
||||
deviceCDCACMEventGetEncapsulatedResponse // This event indicates the device received the GET_ENCAPSULATED_RESPONSE request.
|
||||
deviceCDCACMEventSetCommFeature // This event indicates the device received the SET_COMM_FEATURE request.
|
||||
deviceCDCACMEventGetCommFeature // This event indicates the device received the GET_COMM_FEATURE request.
|
||||
deviceCDCACMEventClearCommFeature // This event indicates the device received the CLEAR_COMM_FEATURE request.
|
||||
deviceCDCACMEventGetLineCoding // This event indicates the device received the GET_LINE_CODING request.
|
||||
deviceCDCACMEventSetLineCoding // This event indicates the device received the SET_LINE_CODING request.
|
||||
deviceCDCACMEventSetControlLineState // This event indicates the device received the SET_CONTRL_LINE_STATE request.
|
||||
deviceCDCACMEventSendBreak // This event indicates the device received the SEND_BREAK request.
|
||||
|
||||
deviceCDCACMRequestNotify = 0xA1
|
||||
|
||||
deviceCDCACMInfoNotifyPacketSize = 8 // (bytes)
|
||||
deviceCDCACMInfoUARTBitmapSize = 2 //
|
||||
deviceCDCACMSerialStateSize = deviceCDCACMInfoNotifyPacketSize + deviceCDCACMInfoUARTBitmapSize
|
||||
)
|
||||
|
||||
var (
|
||||
deviceCDCACMDataSendBuffer = [configDeviceCDCACMCount]deviceCDCACMDataBuffer{}
|
||||
deviceCDCACMDataRecvBuffer = [configDeviceCDCACMCount]deviceCDCACMDataBuffer{}
|
||||
|
||||
deviceCDCACMLineCoding = [configDeviceCDCACMCount][][]uint8{
|
||||
{ // USB CDC-ACM port 0
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // configuration index 1
|
||||
},
|
||||
}
|
||||
|
||||
deviceCDCACMConfigInstance = [configDeviceCDCACMCount][]deviceClassConfig{
|
||||
{{
|
||||
driver: &deviceCDCACM{},
|
||||
info: deviceClassInfo{
|
||||
classID: deviceClassCDC,
|
||||
interfaceList: []deviceClassInterface{
|
||||
{ // USB CDC-ACM communications/control interface
|
||||
classCode: deviceCDCCommClass,
|
||||
subclassCode: deviceCDCAbstractControlModel,
|
||||
protocolCode: deviceCDCNoClassSpecificProtocol,
|
||||
deviceInterface: []deviceInterface{{
|
||||
alternateSetting: 0,
|
||||
endpoint: []deviceEndpoint{
|
||||
{transferType: specEndpointInterrupt},
|
||||
},
|
||||
}},
|
||||
},
|
||||
{ // USB CDC-ACM serial data interface
|
||||
classCode: deviceCDCDataClass,
|
||||
subclassCode: 0x00,
|
||||
protocolCode: deviceCDCNoClassSpecificProtocol,
|
||||
deviceInterface: []deviceInterface{{
|
||||
alternateSetting: 0,
|
||||
endpoint: []deviceEndpoint{
|
||||
{transferType: specEndpointBulk},
|
||||
{transferType: specEndpointBulk},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
deviceCDCACMBufferInvalid32 = leU32(0xFFFFFFFF)
|
||||
)
|
||||
|
||||
func (acm *deviceCDCACM) init(device *device, config *deviceClassConfig, id uint8) status {
|
||||
|
||||
// initialize our associated object references
|
||||
acm.device = device
|
||||
acm.config = config
|
||||
|
||||
// grab references to the global data buffers
|
||||
acm.sendBuffer = &deviceCDCACMDataSendBuffer[device.port]
|
||||
acm.recvBuffer = &deviceCDCACMDataRecvBuffer[device.port]
|
||||
|
||||
// initialize line-coding details from global configuration
|
||||
acm.speed = configDeviceCDCACM[device.port][id-1].interfaceSpeed
|
||||
acm.lineCodingSize = configDeviceCDCACM[device.port][id-1].lineCodingSize
|
||||
acm.baudRate = configDeviceCDCACM[device.port][id-1].lineCodingBaudRate
|
||||
acm.charFormat = configDeviceCDCACM[device.port][id-1].lineCodingCharFormat
|
||||
acm.parityType = configDeviceCDCACM[device.port][id-1].lineCodingParityType
|
||||
acm.dataBits = configDeviceCDCACM[device.port][id-1].lineCodingDataBits
|
||||
|
||||
lineCoding := deviceCDCACMLineCoding[device.port][id-1]
|
||||
lineCoding[0] = uint8(acm.baudRate >> 0)
|
||||
lineCoding[1] = uint8(acm.baudRate >> 8)
|
||||
lineCoding[2] = uint8(acm.baudRate >> 16)
|
||||
lineCoding[3] = uint8(acm.baudRate >> 24)
|
||||
lineCoding[4] = uint8(acm.charFormat)
|
||||
lineCoding[5] = uint8(acm.parityType)
|
||||
lineCoding[6] = uint8(acm.dataBits)
|
||||
|
||||
// initialize remaining state data
|
||||
acm.alternate = 0xFF
|
||||
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (acm *deviceCDCACM) deinit() status {
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (acm *deviceCDCACM) initEndpoints() status {
|
||||
|
||||
// find the communication/control interface in our CDC-ACM configuration list
|
||||
acm.interfaceNumber, acm.comm = acm.findInterface(deviceCDCCommClass)
|
||||
|
||||
// verify we found a valid communication/control interface
|
||||
if nil == acm.comm {
|
||||
return statusInvalidHandle
|
||||
}
|
||||
|
||||
// initialize all of the communication/control input endpoints
|
||||
if s := acm.initInterfaceEndpoints(specIn, specEndpointInterrupt); !s.OK() {
|
||||
return s
|
||||
}
|
||||
|
||||
// find the data interface in our CDC-ACM configuration list
|
||||
_, acm.data = acm.findInterface(deviceCDCDataClass)
|
||||
|
||||
// verify we found a valid data interface
|
||||
if nil == acm.data {
|
||||
return statusInvalidHandle
|
||||
}
|
||||
|
||||
// initialize all of the data input endpoints
|
||||
if s := acm.initInterfaceEndpoints(specIn, specEndpointBulk); !s.OK() {
|
||||
return s
|
||||
}
|
||||
// initialize all of the data output endpoints
|
||||
if s := acm.initInterfaceEndpoints(specOut, specEndpointBulk); !s.OK() {
|
||||
return s
|
||||
}
|
||||
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (acm *deviceCDCACM) initInterfaceEndpoints(direction, transferType uint8) status {
|
||||
|
||||
// select the relevant fields and callbacks for our given interface type
|
||||
pipe, deviceInterface := acm.endpointInterface(direction, transferType)
|
||||
if nil == deviceInterface {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
|
||||
// initialize each endpoint with given direction and transfer type
|
||||
for _, ep := range deviceInterface.endpoint {
|
||||
num, dir := unpackEndpoint(ep.address)
|
||||
if dir == direction && ep.transferType == transferType {
|
||||
pipe.pipeDataBuffer = deviceCDCACMBufferInvalid32
|
||||
pipe.pipeDataLen = 0
|
||||
pipe.pipeStall = false
|
||||
pipe.ep = num
|
||||
pipe.isBusy = false
|
||||
|
||||
if s := acm.device.initEndpoint(&deviceEndpointConfig{
|
||||
maxPacketSize: ep.maxPacketSize,
|
||||
address: ep.address,
|
||||
transferType: ep.transferType,
|
||||
zlt: 0,
|
||||
interval: ep.interval,
|
||||
}, acm, pipe); !s.OK() {
|
||||
return s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (acm *deviceCDCACM) deinitEndpoints() status {
|
||||
|
||||
if nil == acm.device {
|
||||
return statusInvalidHandle
|
||||
}
|
||||
|
||||
if nil != acm.comm {
|
||||
for i := range acm.comm.endpoint {
|
||||
_ = acm.device.deinitEndpoint(acm.comm.endpoint[i].address)
|
||||
}
|
||||
acm.comm = nil
|
||||
}
|
||||
if nil != acm.data {
|
||||
for i := range acm.data.endpoint {
|
||||
_ = acm.device.deinitEndpoint(acm.data.endpoint[i].address)
|
||||
}
|
||||
acm.data = nil
|
||||
}
|
||||
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (acm *deviceCDCACM) event(event deviceClassEventID, param interface{}) (s status) {
|
||||
|
||||
// assume success unless error condition deliberately detected
|
||||
s = statusSuccess
|
||||
|
||||
switch event {
|
||||
case deviceClassEventDeviceReset:
|
||||
// bus reset, clear the selected configuration
|
||||
acm.config = nil
|
||||
|
||||
case deviceClassEventSetConfiguration:
|
||||
if id, ok := param.(uint8); ok {
|
||||
// configuration index is 1-based, meaning configuration 0 is invalid
|
||||
if 0 == id || int(id) > len(acm.device.class.config) {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
if &acm.device.class.config[id-1] == acm.config {
|
||||
break // configuration already selected
|
||||
}
|
||||
// de-initialize endpoints of current configuration
|
||||
if s = acm.deinitEndpoints(); !s.OK() {
|
||||
break
|
||||
}
|
||||
// select new configuration, reset alternate setting
|
||||
acm.config = &acm.device.class.config[id-1]
|
||||
acm.alternate = 0
|
||||
// initialize endpoints of new configuration
|
||||
if s = acm.initEndpoints(); !s.OK() {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
// unexpected parameter, should be uint8 (configuration index)
|
||||
s = statusInvalidParameter
|
||||
}
|
||||
|
||||
case deviceClassEventSetInterface:
|
||||
if interfaceAlternate, ok := param.(uint16); ok {
|
||||
alternate := uint8(interfaceAlternate & 0xFF)
|
||||
if acm.interfaceNumber != uint8(interfaceAlternate>>8) {
|
||||
break // requested alternate from interface different than current
|
||||
}
|
||||
if alternate == acm.alternate {
|
||||
break // requested alternate same as current
|
||||
}
|
||||
// de-initialize endpoints of current configuration
|
||||
if s = acm.deinitEndpoints(); !s.OK() {
|
||||
break
|
||||
}
|
||||
// select new alternate
|
||||
acm.alternate = alternate
|
||||
// initialize endpoints of new configuration
|
||||
if s = acm.initEndpoints(); !s.OK() {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
// unexpected parameter, should be uint16: interface (hi), alternate (lo)
|
||||
s = statusInvalidParameter
|
||||
}
|
||||
|
||||
case deviceClassEventSetEndpointHalt:
|
||||
// verify parameter and fields
|
||||
if address, ok := param.(uint8); ok {
|
||||
if nil != acm.config && nil != acm.comm && nil != acm.data {
|
||||
// check if given endpoint is a communication/control endpoint
|
||||
for _, e := range acm.comm.endpoint {
|
||||
if address == e.address {
|
||||
// found endpoint, set stall flag
|
||||
acm.interruptIn.pipeStall = true
|
||||
// notify device
|
||||
c := acm.device.control(deviceControlEndpointStall, address)
|
||||
if s.OK() && !c.OK() {
|
||||
s = c
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if given endpoint is a data endpoint
|
||||
for _, e := range acm.data.endpoint {
|
||||
if address == e.address {
|
||||
_, direction := unpackEndpoint(address)
|
||||
// found endpoint, set stall flag
|
||||
if specIn == direction {
|
||||
acm.bulkIn.pipeStall = true
|
||||
} else {
|
||||
acm.bulkOut.pipeStall = true
|
||||
}
|
||||
// notify device
|
||||
c := acm.device.control(deviceControlEndpointStall, address)
|
||||
if s.OK() && !c.OK() {
|
||||
s = c
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = statusInvalidHandle
|
||||
}
|
||||
} else {
|
||||
// unexpected parameter, should be uint8 (endpoint address)
|
||||
s = statusInvalidParameter
|
||||
}
|
||||
|
||||
case deviceClassEventClearEndpointHalt:
|
||||
// verify parameter and fields
|
||||
if address, ok := param.(uint8); ok {
|
||||
if nil != acm.config && nil != acm.comm && nil != acm.data {
|
||||
// check if given endpoint is a communication/control endpoint
|
||||
for _, e := range acm.comm.endpoint {
|
||||
if address == e.address {
|
||||
// found endpoint, notify device
|
||||
c := acm.device.control(deviceControlEndpointUnstall, address)
|
||||
if s.OK() && !c.OK() {
|
||||
s = c
|
||||
}
|
||||
_, direction := unpackEndpoint(address)
|
||||
if specIn == direction {
|
||||
// flush any buffered data written to the stalled endpoint
|
||||
if acm.interruptIn.pipeStall {
|
||||
// clear stall flag
|
||||
acm.interruptIn.pipeStall = false
|
||||
// verify the buffer has valid data
|
||||
if !bytes.Equal(acm.interruptIn.pipeDataBuffer, deviceCDCACMBufferInvalid32) {
|
||||
// transmit
|
||||
u := acm.device.send(
|
||||
acm.interruptIn.ep,
|
||||
acm.interruptIn.pipeDataBuffer,
|
||||
acm.interruptIn.pipeDataLen)
|
||||
if !u.OK() {
|
||||
// notify upper layer driver of communication/control event
|
||||
_ = acm.controlEndpoint(
|
||||
deviceEndpointControlMessage{
|
||||
buffer: acm.interruptIn.pipeDataBuffer,
|
||||
length: acm.interruptIn.pipeDataLen,
|
||||
isSetup: false,
|
||||
},
|
||||
&acm.interruptIn,
|
||||
)
|
||||
if s.OK() {
|
||||
s = u
|
||||
}
|
||||
}
|
||||
// clear the stalled endpoint buffer
|
||||
acm.interruptIn.pipeDataBuffer = deviceCDCACMBufferInvalid32
|
||||
acm.interruptIn.pipeDataLen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if given endpoint is a data endpoint
|
||||
for _, e := range acm.data.endpoint {
|
||||
if address == e.address {
|
||||
// found endpoint, notify device
|
||||
c := acm.device.control(deviceControlEndpointUnstall, address)
|
||||
if s.OK() && !c.OK() {
|
||||
s = c
|
||||
}
|
||||
// check if endpoint is an input or output
|
||||
_, direction := unpackEndpoint(address)
|
||||
if specIn == direction {
|
||||
// flush any buffered data written to the stalled endpoint
|
||||
if acm.bulkIn.pipeStall {
|
||||
// clear stall flag
|
||||
acm.bulkIn.pipeStall = false
|
||||
// verify the buffer has valid data
|
||||
if !bytes.Equal(acm.bulkIn.pipeDataBuffer, deviceCDCACMBufferInvalid32) {
|
||||
// transmit
|
||||
u := acm.device.send(
|
||||
acm.bulkIn.ep,
|
||||
acm.bulkIn.pipeDataBuffer,
|
||||
acm.bulkIn.pipeDataLen)
|
||||
if !u.OK() {
|
||||
// notify upper layer driver of data input event
|
||||
_ = acm.controlEndpoint(
|
||||
deviceEndpointControlMessage{
|
||||
buffer: acm.bulkIn.pipeDataBuffer,
|
||||
length: acm.bulkIn.pipeDataLen,
|
||||
isSetup: false,
|
||||
},
|
||||
&acm.bulkIn,
|
||||
)
|
||||
if s.OK() {
|
||||
s = u
|
||||
}
|
||||
}
|
||||
// clear the stalled endpoint buffer
|
||||
acm.bulkIn.pipeDataBuffer = deviceCDCACMBufferInvalid32
|
||||
acm.bulkIn.pipeDataLen = 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// flush any buffered data read from the stalled endpoint
|
||||
if acm.bulkOut.pipeStall {
|
||||
// clear stall flag
|
||||
acm.bulkOut.pipeStall = false
|
||||
// verify the buffer has valid data
|
||||
if !bytes.Equal(acm.bulkOut.pipeDataBuffer, deviceCDCACMBufferInvalid32) {
|
||||
// receive
|
||||
u := acm.device.receive(
|
||||
acm.bulkOut.ep,
|
||||
acm.bulkOut.pipeDataBuffer,
|
||||
acm.bulkOut.pipeDataLen)
|
||||
if !u.OK() {
|
||||
// notify upper layer driver of data output event
|
||||
_ = acm.controlEndpoint(
|
||||
deviceEndpointControlMessage{
|
||||
buffer: acm.bulkOut.pipeDataBuffer,
|
||||
length: acm.bulkOut.pipeDataLen,
|
||||
isSetup: false,
|
||||
},
|
||||
&acm.bulkOut,
|
||||
)
|
||||
if s.OK() {
|
||||
s = u
|
||||
}
|
||||
}
|
||||
// clear the stalled endpoint buffer
|
||||
acm.bulkOut.pipeDataBuffer = deviceCDCACMBufferInvalid32
|
||||
acm.bulkOut.pipeDataLen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = statusInvalidHandle
|
||||
}
|
||||
} else {
|
||||
// unexpected parameter, should be uint8 (endpoint address)
|
||||
s = statusInvalidParameter
|
||||
}
|
||||
case deviceClassEventClassRequest:
|
||||
// verify parameter and fields
|
||||
if request, ok := param.(*deviceControlRequest); ok {
|
||||
// verify requested interface is receiver's interface and request is CDC
|
||||
if (request.setup.wIndex&0xFF) != uint16(acm.interfaceNumber) ||
|
||||
(request.setup.bmRequestType&specRequestTypeTypeMsk) != specRequestTypeTypeClass {
|
||||
// construct standard parameter to be passed on to upper layer drivevr
|
||||
param := deviceCDCACMRequestParam{
|
||||
buffer: &request.buffer,
|
||||
length: &request.length,
|
||||
interfaceIndex: request.setup.wIndex,
|
||||
setupValue: request.setup.wValue,
|
||||
isSetup: request.isSetup,
|
||||
}
|
||||
// translate request code to event code
|
||||
var event deviceCDCACMEventID
|
||||
switch request.setup.bRequest {
|
||||
case deviceCDCRequestSendEncapsulatedCommand:
|
||||
event = deviceCDCACMEventSendEncapsulatedCommand
|
||||
case deviceCDCRequestGetEncapsulatedResponse:
|
||||
event = deviceCDCACMEventGetEncapsulatedResponse
|
||||
case deviceCDCRequestSetCommFeature:
|
||||
event = deviceCDCACMEventSetCommFeature
|
||||
case deviceCDCRequestGetCommFeature:
|
||||
event = deviceCDCACMEventGetCommFeature
|
||||
case deviceCDCRequestClearCommFeature:
|
||||
event = deviceCDCACMEventClearCommFeature
|
||||
case deviceCDCRequestGetLineCoding:
|
||||
event = deviceCDCACMEventGetLineCoding
|
||||
case deviceCDCRequestSetLineCoding:
|
||||
event = deviceCDCACMEventSetLineCoding
|
||||
case deviceCDCRequestSetControlLineState:
|
||||
event = deviceCDCACMEventSetControlLineState
|
||||
case deviceCDCRequestSendBreak:
|
||||
event = deviceCDCACMEventSendBreak
|
||||
default:
|
||||
s = statusInvalidRequest
|
||||
}
|
||||
// notify request to upper layer driver
|
||||
if nil != acm.device && nil != acm.device.class &&
|
||||
nil != acm.device.class.handler {
|
||||
acm.device.class.handler.classEvent(uint32(event), param)
|
||||
}
|
||||
} else {
|
||||
s = statusInvalidRequest
|
||||
}
|
||||
} else {
|
||||
s = statusInvalidParameter
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (acm *deviceCDCACM) send(ep uint8, buffer []uint8, length uint32) status {
|
||||
var pipe *deviceCDCACMPipe
|
||||
// select which endpoint pipe to write into
|
||||
switch ep {
|
||||
case acm.interruptIn.ep:
|
||||
pipe = &acm.interruptIn
|
||||
case acm.bulkIn.ep:
|
||||
pipe = &acm.bulkIn
|
||||
default:
|
||||
return statusInvalidParameter
|
||||
}
|
||||
if pipe.isBusy {
|
||||
return statusBusy
|
||||
}
|
||||
// set pipe busy flag
|
||||
pipe.isBusy = true
|
||||
// check if pipe stall flag is set
|
||||
if pipe.pipeStall {
|
||||
// write data to pipe buffer
|
||||
pipe.pipeDataBuffer = buffer
|
||||
pipe.pipeDataLen = length
|
||||
return statusSuccess
|
||||
}
|
||||
// pass data to device for transmission
|
||||
if s := acm.device.send(ep, buffer, length); !s.OK() {
|
||||
pipe.isBusy = false
|
||||
return s
|
||||
}
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (acm *deviceCDCACM) receive(ep uint8, buffer []uint8, length uint32) status {
|
||||
var pipe *deviceCDCACMPipe
|
||||
// select which endpoint pipe to read from
|
||||
switch ep {
|
||||
case acm.bulkOut.ep:
|
||||
pipe = &acm.bulkOut
|
||||
default:
|
||||
return statusInvalidParameter
|
||||
}
|
||||
if pipe.isBusy {
|
||||
return statusBusy
|
||||
}
|
||||
// set pipe busy flag
|
||||
pipe.isBusy = true
|
||||
// check if pipe stall flag is set
|
||||
if pipe.pipeStall {
|
||||
// write data to pipe buffer
|
||||
pipe.pipeDataBuffer = buffer
|
||||
pipe.pipeDataLen = length
|
||||
return statusSuccess
|
||||
}
|
||||
// pass data to device for reception
|
||||
if s := acm.device.receive(ep, buffer, length); !s.OK() {
|
||||
pipe.isBusy = false
|
||||
return s
|
||||
}
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
// findInterface returns the interface number and deviceInterface from the
|
||||
// receiver's CDC-ACM configuration list whose classCode and alternateSetting
|
||||
// equals the given classCode and receiver's current alternateSetting;
|
||||
// otherwise, no such interface is found, returns 0 and nil.
|
||||
func (acm *deviceCDCACM) findInterface(classCode uint8) (uint8, *deviceInterface) {
|
||||
if nil == acm.device || nil == acm.config {
|
||||
return 0, nil
|
||||
}
|
||||
for c := range acm.config.info.interfaceList {
|
||||
dci := &acm.config.info.interfaceList[c]
|
||||
if classCode == dci.classCode {
|
||||
for d := range dci.deviceInterface {
|
||||
if acm.alternate == dci.deviceInterface[d].alternateSetting {
|
||||
return dci.interfaceNumber, &dci.deviceInterface[d]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (acm *deviceCDCACM) endpointInterface(
|
||||
direction, transferType uint8) (*deviceCDCACMPipe, *deviceInterface) {
|
||||
|
||||
switch transferType {
|
||||
case specEndpointInterrupt:
|
||||
switch direction {
|
||||
case specIn:
|
||||
return &acm.interruptIn, acm.comm
|
||||
}
|
||||
case specEndpointBulk:
|
||||
switch direction {
|
||||
case specIn:
|
||||
return &acm.bulkIn, acm.data
|
||||
case specOut:
|
||||
return &acm.bulkOut, acm.data
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (acm *deviceCDCACM) controlEndpoint(
|
||||
message deviceEndpointControlMessage, param interface{}) status {
|
||||
|
||||
if pipe, ok := param.(*deviceCDCACMPipe); ok {
|
||||
var event deviceCDCACMEventID
|
||||
switch pipe {
|
||||
case &acm.interruptIn:
|
||||
event = deviceCDCACMEventSerialStateNotify
|
||||
case &acm.bulkIn:
|
||||
event = deviceCDCACMEventSendResponse
|
||||
case &acm.bulkOut:
|
||||
event = deviceCDCACMEventRecvResponse
|
||||
}
|
||||
pipe.isBusy = false
|
||||
if nil != acm.device && nil != acm.device.class &&
|
||||
nil != acm.device.class.handler {
|
||||
acm.device.class.handler.classEvent(uint32(event), message)
|
||||
return statusSuccess
|
||||
}
|
||||
}
|
||||
return statusInvalidParameter
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
package usb
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// deviceController provides hardware abstraction over a platform's physical
|
||||
// USB device port controller (e.g., an EHCI-compliant peripheral).
|
||||
//
|
||||
// To connect the USB 2.0 device driver stack to a particular platform, the
|
||||
// following method must be implemented:
|
||||
//
|
||||
// func (d *device) initController() deviceController
|
||||
//
|
||||
// This method shall return an implementation of deviceController, or nil if a
|
||||
// controller cannot be allocated for the given port.
|
||||
type (
|
||||
deviceController interface {
|
||||
init() status // Controller initialization
|
||||
deinit() status // Controller de-initialization
|
||||
enable(enable bool) status // Controller enable runtime
|
||||
interrupt() // Controller interrupt handler
|
||||
process() status // Controller process interrupts
|
||||
send(address uint8, buffer []uint8, length uint32) status // Controller send data
|
||||
receive(address uint8, buffer []uint8, length uint32) status // Controller receive data
|
||||
cancel(address uint8) status // Controller cancel transfer
|
||||
control(command deviceControlID, param interface{}) status // Controller device control
|
||||
critical(enter bool) status // Controller critical section
|
||||
}
|
||||
|
||||
// deviceControllerInterruptQueue represents a fixed-length, circular queue
|
||||
// (FIFO) of interrupts.
|
||||
//
|
||||
// The enqueue and dequeue operations (methods enq and deq, respectively) are
|
||||
// NOT interrupt-/thread-safe. The user must protect against race conditions
|
||||
// using appropriate resources for their system. For example, the interface
|
||||
// method (deviceController).critical(bool) implements the concept of critical
|
||||
// sections, which could be used to prevent concurrent accesses.
|
||||
// This restriction also applies to the higher-level methods fill and drain.
|
||||
deviceControllerInterruptQueue struct {
|
||||
queue [configInterruptQueueSize]uintptr // circular queue
|
||||
head int
|
||||
count int
|
||||
}
|
||||
|
||||
deviceControllerCapabilitiesBitmap uint32
|
||||
deviceControllerCapabilities struct {
|
||||
reserved1 uint16 // 15 (bits)
|
||||
ios uint8 // 1
|
||||
maxPacketSize uint16 // 11
|
||||
reserved2 uint8 // 2
|
||||
zlt uint8 // 1
|
||||
mult uint8 // 2 (= 32 bits)
|
||||
}
|
||||
|
||||
deviceControllerDTDTokenBitmap uint32
|
||||
deviceControllerDTDToken struct {
|
||||
status uint8 // 8 (bits)
|
||||
reserved1 uint8 // 2
|
||||
multiplierOverride uint8 // 2
|
||||
reserved2 uint8 // 3
|
||||
ioc uint8 // 1
|
||||
totalBytes uint16 // 15
|
||||
reserved3 uint8 // 1 (= 32 bits)
|
||||
}
|
||||
|
||||
deviceControllerEndpointStatusBitmap uint32
|
||||
deviceControllerEndpointStatus struct {
|
||||
isOpened uint8 // 1 (bits)
|
||||
zlt uint8 // 1
|
||||
_ uint32 // 30 (= 32 bits)
|
||||
}
|
||||
|
||||
deviceControllerOriginalBufferBitmap uint32
|
||||
deviceControllerOriginalBuffer struct {
|
||||
originalBufferOffset uint16 // 12 (bits)
|
||||
originalBufferLength uint32 // 19
|
||||
dtdInvalid uint8 // 1 (= 32 bits)
|
||||
}
|
||||
|
||||
deviceControllerQH struct {
|
||||
capabilities deviceControllerCapabilitiesBitmap // 4 (bytes)
|
||||
currentDTDPointer *deviceControllerDTD // 4
|
||||
nextDTDPointer *deviceControllerDTD // 4
|
||||
dtdToken deviceControllerDTDTokenBitmap // 4
|
||||
bufferPointerPage [5]uint32 // 20
|
||||
reserved1 uint32 // 4
|
||||
setupBuffer deviceSetupBuffer // 8
|
||||
setupBufferBack deviceSetupBuffer // 8
|
||||
endpointStatus deviceControllerEndpointStatusBitmap // 4
|
||||
reserved2 uint32 // 4 (= 64 bytes)
|
||||
}
|
||||
|
||||
deviceControllerDTDList [2 * configDeviceMaxEndpoints]*deviceControllerDTD
|
||||
deviceControllerDTD struct {
|
||||
nextDTDPointer *deviceControllerDTD // 4 (bytes)
|
||||
dtdToken deviceControllerDTDTokenBitmap // 4
|
||||
bufferPointerPage [5]uint32 // 20
|
||||
originalBuffer deviceControllerOriginalBufferBitmap // 4 (= 32 bytes)
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// device QH
|
||||
deviceControllerQHSize = 64 // bytes
|
||||
deviceControllerQHBufferSize = (configDeviceCount-1)*configDeviceControllerQHAlign +
|
||||
2*configDeviceMaxEndpoints*2*deviceControllerQHSize
|
||||
|
||||
deviceControllerQHPointerMsk = 0xFFFFFFC0
|
||||
deviceControllerQHMultMsk = 0xC0000000
|
||||
deviceControllerQHZLTMsk = 0x20000000
|
||||
deviceControllerQHMaxPacketSizeMsk = 0x07FF0000
|
||||
deviceControllerQHMaxPacketSize = 0x00000800
|
||||
deviceControllerQHIOSMsk = 0x00008000
|
||||
|
||||
// device DTD
|
||||
deviceControllerDTDSize = 32 // bytes
|
||||
deviceControllerDTDBufferSize = (configDeviceCount-1)*configDeviceControllerDTDAlign +
|
||||
configDeviceControllerMaxDTD*deviceControllerDTDSize
|
||||
|
||||
deviceControllerDTDPointerMsk = 0xFFFFFFE0
|
||||
deviceControllerDTDTerminateMsk = 0x00000001
|
||||
deviceControllerDTDPageMsk = 0xFFFFF000
|
||||
deviceControllerDTDPageOffsetMsk = 0x00000FFF
|
||||
deviceControllerDTDPageBlock = 0x00001000
|
||||
deviceControllerDTDTotalBytesMsk = 0x7FFF0000
|
||||
deviceControllerDTDTotalBytes = 0x00004000
|
||||
deviceControllerDTDIOCMsk = 0x00008000
|
||||
deviceControllerDTDMultIOMsk = 0x00000C00
|
||||
deviceControllerDTDStatusMsk = 0x000000FF
|
||||
deviceControllerDTDStatusErrorMsk = 0x00000068
|
||||
deviceControllerDTDStatusActive = 0x00000080
|
||||
deviceControllerDTDStatusHalted = 0x00000040
|
||||
deviceControllerDTDStatusDataBufferError = 0x00000020
|
||||
deviceControllerDTDStatusTransactionError = 0x00000008
|
||||
)
|
||||
|
||||
var (
|
||||
// special invalid pointer, indicating the end of a list of DTDs
|
||||
deviceControllerDTDTerminate = (*deviceControllerDTD)(unsafe.Pointer(uintptr(
|
||||
deviceControllerDTDTerminateMsk)))
|
||||
)
|
||||
|
||||
// enq enqueues the given mask into tail position of the receiver iq's queue,
|
||||
// increasing queue length by 1.
|
||||
//
|
||||
// When the queue fills to capacity, any subsequent enqueue will overwrite the
|
||||
// current head with the given value, positioning its following element at the
|
||||
// front of the queue, and leaves queue length unaffected.
|
||||
func (iq *deviceControllerInterruptQueue) enq(mask uintptr) {
|
||||
if iq.count == configInterruptQueueSize {
|
||||
// queue is full; overwrite oldest element (queue head) and increment head
|
||||
iq.queue[iq.head] = mask
|
||||
iq.head++
|
||||
iq.head %= configInterruptQueueSize
|
||||
} else {
|
||||
// queue is not full; place element at queue tail and increment count
|
||||
iq.queue[(iq.head+iq.count)%configInterruptQueueSize] = mask
|
||||
iq.count++
|
||||
}
|
||||
}
|
||||
|
||||
// deq dequeues the mask in tail position from the receiver iq's queue, reducing
|
||||
// queue length by 1, and returns the dequeued value with true.
|
||||
//
|
||||
// When the queue is empty, queue length remains unaffected, and it returns 0
|
||||
// with false.
|
||||
func (iq *deviceControllerInterruptQueue) deq() (uintptr, bool) {
|
||||
if iq.count == 0 {
|
||||
// queue is empty; reset head and return an invalid value
|
||||
iq.head = 0
|
||||
return 0, false
|
||||
}
|
||||
// queue is not empty; decrement count, reset and return value at queue tail
|
||||
iq.count--
|
||||
n := (iq.head + iq.count) % configInterruptQueueSize
|
||||
m := iq.queue[n]
|
||||
iq.queue[n] = 0 // ensure the queue contains no spurious data
|
||||
return m, true
|
||||
}
|
||||
|
||||
// fill enqueues each given mask (in order) into tail position of the receiver
|
||||
// iq's queue, increasing queue length up to capacity, if possible.
|
||||
//
|
||||
// If the number of values given is greater than available queue positions, each
|
||||
// element in head position - at the time the value is enqueued - will be
|
||||
// overwritten, so that queue length never exceeds capacity, and the element in
|
||||
// tail position is always the final element given.
|
||||
func (iq *deviceControllerInterruptQueue) fill(mask ...uintptr) {
|
||||
for _, m := range mask {
|
||||
iq.enq(m)
|
||||
}
|
||||
}
|
||||
|
||||
// drain dequeues all elements in the receiver iq's queue, reducing queue length
|
||||
// to 0, and returns the dequeued values (in order) and the number of number of
|
||||
// values dequeued.
|
||||
func (iq *deviceControllerInterruptQueue) drain() (
|
||||
q [configInterruptQueueSize]uintptr, n int,
|
||||
) {
|
||||
for {
|
||||
m, ok := iq.deq()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
q[n] = m
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
func getQHBuffer(port uint8, qh int, ep int) *deviceControllerQH {
|
||||
if port < configDeviceCount && qh < 2 && ep < 2*configDeviceMaxEndpoints {
|
||||
return (*deviceControllerQH)(unsafe.Pointer(
|
||||
&deviceControllerQHBuffer[int(port)*configDeviceControllerQHAlign+
|
||||
qh*2*configDeviceMaxEndpoints*deviceControllerQHSize+
|
||||
ep*deviceControllerQHSize]))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDTDBuffer(port uint8, dtd int) *deviceControllerDTD {
|
||||
if port < configDeviceCount && dtd < configDeviceControllerMaxDTD {
|
||||
return (*deviceControllerDTD)(unsafe.Pointer(
|
||||
&deviceControllerDTDBuffer[int(port)*configDeviceControllerDTDAlign+
|
||||
dtd*deviceControllerDTDSize]))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s deviceControllerCapabilities) pack() deviceControllerCapabilitiesBitmap {
|
||||
return deviceControllerCapabilitiesBitmap(
|
||||
((uint32(s.reserved1) & 0x7FFF) << 0) | // uint16 // 15 (bits)
|
||||
((uint32(s.ios) & 0x1) << 15) | // uint8 // 1
|
||||
((uint32(s.maxPacketSize) & 0x7FF) << 16) | // uint16 // 11
|
||||
((uint32(s.reserved2) & 0x3) << 27) | // uint8 // 2
|
||||
((uint32(s.zlt) & 0x1) << 29) | // uint8 // 1
|
||||
((uint32(s.mult) & 0x3) << 30)) // uint8 // 2 (= 32 bits)
|
||||
}
|
||||
|
||||
func (s deviceControllerDTDToken) pack() deviceControllerDTDTokenBitmap {
|
||||
return deviceControllerDTDTokenBitmap(
|
||||
((uint32(s.status) & 0xFF) << 0) | // uint8 // 8 (bits)
|
||||
((uint32(s.reserved1) & 0x3) << 8) | // uint8 // 2
|
||||
((uint32(s.multiplierOverride) & 0x3) << 10) | // uint8 // 2
|
||||
((uint32(s.reserved2) & 0x7) << 12) | // uint8 // 3
|
||||
((uint32(s.ioc) & 0x1) << 15) | // uint8 // 1
|
||||
((uint32(s.totalBytes) & 0x7FFF) << 16) | // uint16 // 15
|
||||
((uint32(s.reserved3) & 0x1) << 31)) // uint8 // 1 (= 32 bits)
|
||||
}
|
||||
|
||||
func (b deviceControllerDTDTokenBitmap) unpack() deviceControllerDTDToken {
|
||||
return deviceControllerDTDToken{
|
||||
status: uint8(b>>0) & 0xFF,
|
||||
reserved1: uint8(b>>8) & 0x3,
|
||||
multiplierOverride: uint8(b>>10) & 0x3,
|
||||
reserved2: uint8(b>>12) & 0x7,
|
||||
ioc: uint8(b>>15) & 0x1,
|
||||
totalBytes: uint16(b>>16) & 0x7FFF,
|
||||
reserved3: uint8(b>>31) & 0x1,
|
||||
}
|
||||
}
|
||||
|
||||
func (s deviceControllerEndpointStatus) pack() deviceControllerEndpointStatusBitmap {
|
||||
return deviceControllerEndpointStatusBitmap(
|
||||
((uint32(s.isOpened) & 0x1) << 0) | // uint8 // 1 (bits)
|
||||
((uint32(s.zlt) & 0x1) << 1)) // uint8 // 1
|
||||
}
|
||||
|
||||
func (s deviceControllerOriginalBuffer) pack() deviceControllerOriginalBufferBitmap {
|
||||
return deviceControllerOriginalBufferBitmap(
|
||||
((uint32(s.originalBufferOffset) & 0xFFF) << 0) | // uint16 // 12 (bits)
|
||||
((uint32(s.originalBufferLength) & 0x7FFFF) << 12) | // uint32 // 19
|
||||
((uint32(s.dtdInvalid) & 0x1) << 31)) // uint8 // 1 (= 32 bits)
|
||||
}
|
||||
|
||||
func (b deviceControllerOriginalBufferBitmap) unpack() deviceControllerOriginalBuffer {
|
||||
return deviceControllerOriginalBuffer{
|
||||
originalBufferOffset: uint16(b>>0) & 0xFFF,
|
||||
originalBufferLength: uint32(b>>12) & 0x7FFFF,
|
||||
dtdInvalid: uint8(b>>31) & 0x1,
|
||||
}
|
||||
}
|
||||
|
||||
// cycles converts the given number of microseconds to CPU cycles for a CPU with
|
||||
// given frequency.
|
||||
//go:inline
|
||||
func cycles(microsec, cpuFreqHz uint32) uint32 {
|
||||
return uint32((uint64(microsec) * uint64(cpuFreqHz)) / 1000000)
|
||||
}
|
||||
@@ -1,709 +0,0 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb
|
||||
|
||||
// Implementation of a port controller for USB device mode on NXP iMXRT1062.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nxp"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// deviceControl represents a USB device controller for NXP iMXRT1062.
|
||||
// It implements the USB API's deviceController interface.
|
||||
type deviceControl struct {
|
||||
port uint8
|
||||
device *device
|
||||
irq interrupt.Interrupt
|
||||
|
||||
messages deviceControllerInterruptQueue
|
||||
|
||||
interruptMask uintptr // interrupt state upon entering critical section
|
||||
criticalState volatile.Register8 // set to 1 if in critical section, else 0
|
||||
|
||||
bus *nxp.USB_Type
|
||||
phy *nxp.USBPHY_Type
|
||||
nc *nxp.USBNC_Type
|
||||
qh *deviceControllerQH // The QH structure base address
|
||||
dtd *deviceControllerDTD // The DTD structure base address
|
||||
dtdFree *deviceControllerDTD // The idle DTD list head
|
||||
dtdHead deviceControllerDTDList // The transferring DTD list head for each endpoint
|
||||
dtdTail deviceControllerDTDList // The transferring DTD list tail for each endpoint
|
||||
dtdCount uint8 // The idle DTD node count
|
||||
endpointCount uint8 // The endpoint number of EHCI
|
||||
isResetting bool // Whether a PORT reset is occurring or not
|
||||
controllerId uint8 // Controller ID
|
||||
speed uint8 // Current speed of EHCI
|
||||
isSuspending bool // Is suspending of the PORT
|
||||
}
|
||||
|
||||
var (
|
||||
// deviceControlInstance holds instances for all USB device controllers
|
||||
// available on the platform.
|
||||
deviceControlInstance [configDeviceCount]deviceControl
|
||||
|
||||
//go:align 2048
|
||||
deviceControllerQHBuffer [deviceControllerQHBufferSize]uint8
|
||||
//go:align 32
|
||||
deviceControllerDTDBuffer [deviceControllerDTDBufferSize]uint8
|
||||
)
|
||||
|
||||
// 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 delayMicrosec(microsec uint32) {
|
||||
n := cycles(microsec, configCPUFrequencyHz)
|
||||
for i := uint32(0); i < n; i++ {
|
||||
arm.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
|
||||
// initController returns a deviceController for the receiver USB device.
|
||||
// It allocates the registers and installs/enables an interrupt handler for
|
||||
// the receiver USB device. It also initializes the shared buffers used by the
|
||||
// USB controller hardware.
|
||||
func (d *device) initController() deviceController {
|
||||
|
||||
dc := &deviceControlInstance[d.port]
|
||||
|
||||
dc.port = d.port
|
||||
dc.device = d
|
||||
|
||||
// based on the selected port, install interrupt handler and initialize
|
||||
// register references
|
||||
switch d.port {
|
||||
case 0:
|
||||
dc.irq = interrupt.New(nxp.IRQ_USB_OTG1, func(interrupt.Interrupt) {
|
||||
portInstance[0].device.controller.interrupt()
|
||||
})
|
||||
dc.bus = nxp.USB1
|
||||
dc.phy = nxp.USBPHY1
|
||||
dc.nc = nxp.USBNC1
|
||||
case 1:
|
||||
dc.irq = interrupt.New(nxp.IRQ_USB_OTG2, func(interrupt.Interrupt) {
|
||||
//portInstance[1].device.controller.interrupt()
|
||||
})
|
||||
dc.bus = nxp.USB2
|
||||
dc.phy = nxp.USBPHY2
|
||||
dc.nc = nxp.USBNC2
|
||||
}
|
||||
|
||||
// get base address of QH and DTD buffers
|
||||
dc.qh = (*deviceControllerQH)(unsafe.Pointer(
|
||||
&deviceControllerQHBuffer[int(d.port)*configDeviceControllerQHAlign]))
|
||||
dc.dtd = (*deviceControllerDTD)(unsafe.Pointer(
|
||||
&deviceControllerDTDBuffer[int(d.port)*configDeviceControllerDTDAlign]))
|
||||
|
||||
dc.irq.SetPriority(configInterruptPriority)
|
||||
dc.irq.Enable()
|
||||
|
||||
return dc
|
||||
}
|
||||
|
||||
// init initializes the USB device subsystem for the receiver deviceControl.
|
||||
func (dc *deviceControl) init() status {
|
||||
|
||||
// reset the controller
|
||||
dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST)
|
||||
for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) {
|
||||
}
|
||||
|
||||
// get hardware's endpoint count
|
||||
dc.endpointCount = uint8(dc.bus.DCCPARAMS.Get() & nxp.USB_DCCPARAMS_DEN_Msk)
|
||||
if dc.endpointCount < configDeviceMaxEndpoints {
|
||||
return statusError
|
||||
}
|
||||
|
||||
// clear the controller mode field and set to device mode:
|
||||
// controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only
|
||||
dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2,
|
||||
nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos)
|
||||
|
||||
// reset the constroller state to default
|
||||
return dc.resetState()
|
||||
}
|
||||
|
||||
func (dc *deviceControl) deinit() status {
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (dc *deviceControl) enable(enable bool) status {
|
||||
if enable {
|
||||
// ensure D+ pulled down long enough for host to detect previous disconnect
|
||||
delayMicrosec(5000)
|
||||
return dc.control(deviceControlRun, nil)
|
||||
} else {
|
||||
return dc.control(deviceControlStop, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// interrupt is the base interrupt handler for all USB device interrupts.
|
||||
func (dc *deviceControl) interrupt() {
|
||||
|
||||
// protect access to message queue
|
||||
for statusRetry == dc.critical(true) {
|
||||
}
|
||||
|
||||
// read and clear the interrupts that fired
|
||||
status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get()
|
||||
dc.bus.USBSTS.Set(status)
|
||||
|
||||
// enqueue interrupts for runtime processing
|
||||
dc.messages.enq(uintptr(status))
|
||||
|
||||
// release message queue
|
||||
_ = dc.critical(false)
|
||||
}
|
||||
|
||||
func (dc *deviceControl) process() status {
|
||||
|
||||
// protect access to message queue
|
||||
if dc.critical(true).OK() {
|
||||
|
||||
// dequeue oldest interrupt in message queue
|
||||
status, ok := dc.messages.deq()
|
||||
|
||||
// release message queue
|
||||
_ = dc.critical(false)
|
||||
|
||||
// process message if queue was not empty
|
||||
if ok {
|
||||
|
||||
if 0 != (status & nxp.USB_USBSTS_URI_Msk) { // USB reset
|
||||
dc.reset()
|
||||
}
|
||||
|
||||
if 0 != (status & nxp.USB_USBSTS_UI_Msk) { // USB token done
|
||||
dc.tokenDone()
|
||||
}
|
||||
|
||||
if 0 != (status & nxp.USB_USBSTS_PCI_Msk) { // USB port status change
|
||||
dc.portChange()
|
||||
}
|
||||
|
||||
if 0 != (status & nxp.USB_USBSTS_SRI_Msk) { // USB start of frame (SOF)
|
||||
dc.frameStart()
|
||||
}
|
||||
}
|
||||
|
||||
// message queue read and processed
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
// could not acquire lock on message queue
|
||||
return statusBusy
|
||||
}
|
||||
|
||||
func (dc *deviceControl) transfer(address uint8, buffer []uint8, length uint32) status {
|
||||
|
||||
if dc.isResetting {
|
||||
return statusError
|
||||
}
|
||||
|
||||
endpoint, direction := unpackEndpoint(address)
|
||||
endpointIndex := int((endpoint << 1) | direction)
|
||||
currentIndex := 0
|
||||
|
||||
primeBit := uint32(1) << ((address & specDescriptorEndpointAddressNumberMsk) +
|
||||
((address & specDescriptorEndpointAddressDirectionMsk) >> 3))
|
||||
epStatus, qhIdle := primeBit, false
|
||||
|
||||
qh := getQHBuffer(dc.port, 0, endpointIndex)
|
||||
if 0 == qh.endpointStatus&0x1 { // bit 0: isOpened
|
||||
return statusError
|
||||
}
|
||||
|
||||
dtdRequestCount := (length + deviceControllerDTDTotalBytes - 1) /
|
||||
deviceControllerDTDTotalBytes
|
||||
if 0 == dtdRequestCount {
|
||||
dtdRequestCount = 1
|
||||
}
|
||||
|
||||
if dtdRequestCount > uint32(dc.dtdCount) {
|
||||
return statusBusy
|
||||
}
|
||||
|
||||
var (
|
||||
dtdHead *deviceControllerDTD
|
||||
sendLength uint32
|
||||
)
|
||||
|
||||
for {
|
||||
|
||||
// limit transfer length to total DTD bytes
|
||||
sendLength = length
|
||||
if length > deviceControllerDTDTotalBytes {
|
||||
sendLength = deviceControllerDTDTotalBytes
|
||||
}
|
||||
length -= sendLength
|
||||
|
||||
// select a free DTD
|
||||
dtd := dc.dtdFree
|
||||
dc.dtdFree = dtd.nextDTDPointer
|
||||
dc.dtdCount--
|
||||
|
||||
// save DTD head when current active buffer offset is 0
|
||||
if 0 == currentIndex {
|
||||
dtdHead = dtd
|
||||
}
|
||||
|
||||
// set DTD field
|
||||
dtd.nextDTDPointer = deviceControllerDTDTerminate
|
||||
dtd.bufferPointerPage[0] =
|
||||
uint32(uintptr(unsafe.Pointer(&buffer[0]))) + uint32(currentIndex)
|
||||
dtd.bufferPointerPage[1] =
|
||||
(dtd.bufferPointerPage[0] + deviceControllerDTDPageBlock) & deviceControllerDTDPageMsk
|
||||
dtd.bufferPointerPage[2] =
|
||||
dtd.bufferPointerPage[1] + deviceControllerDTDPageBlock
|
||||
dtd.bufferPointerPage[3] =
|
||||
dtd.bufferPointerPage[2] + deviceControllerDTDPageBlock
|
||||
dtd.bufferPointerPage[4] =
|
||||
dtd.bufferPointerPage[3] + deviceControllerDTDPageBlock
|
||||
|
||||
// save original buffer and length to transfer
|
||||
dtd.originalBuffer = deviceControllerOriginalBuffer{
|
||||
originalBufferOffset: uint16(dtd.bufferPointerPage[0]) &
|
||||
deviceControllerDTDPageOffsetMsk,
|
||||
originalBufferLength: sendLength,
|
||||
dtdInvalid: 0,
|
||||
}.pack()
|
||||
|
||||
// set IOC field in final DTD
|
||||
ioc := uint8(0)
|
||||
if 0 == length {
|
||||
ioc = 1
|
||||
}
|
||||
// set DTD active flag
|
||||
dtd.dtdToken = deviceControllerDTDToken{
|
||||
status: deviceControllerDTDStatusActive,
|
||||
ioc: ioc,
|
||||
totalBytes: uint16(sendLength),
|
||||
}.pack()
|
||||
|
||||
// update buffer offset
|
||||
currentIndex += int(sendLength)
|
||||
|
||||
// add DTD to in-use queue
|
||||
if nil != dc.dtdTail[endpointIndex] {
|
||||
dc.dtdTail[endpointIndex].nextDTDPointer = dtd
|
||||
dc.dtdTail[endpointIndex] = dtd
|
||||
} else {
|
||||
dc.dtdHead[endpointIndex] = dtd
|
||||
dc.dtdTail[endpointIndex] = dtd
|
||||
qhIdle = true
|
||||
}
|
||||
|
||||
if 0 == length {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if specEndpointControl == endpoint && specIn == direction {
|
||||
// get last setup packet
|
||||
setupIndex := int(endpoint << 1)
|
||||
setupQH := getQHBuffer(dc.port, 0, setupIndex)
|
||||
setupMaxSize := uint32(setupQH.capabilities&0x07FF0000) >> 16 // bits 15-26: maxPacketSize
|
||||
var setup deviceSetup
|
||||
setup.parse(setupQH.setupBufferBack[:])
|
||||
if 0 != qh.endpointStatus&0x2 { // bit 1: ZLT
|
||||
if (0 != sendLength) && (sendLength < uint32(setup.wLength)) &&
|
||||
(0 == (sendLength % setupMaxSize)) {
|
||||
// enable ZLT (zlt==0)
|
||||
setupQH.capabilities &^= deviceControllerCapabilities{zlt: 1}.pack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if QH is empty
|
||||
if !qhIdle {
|
||||
// if prime bit is set, nothing left to do
|
||||
if dc.bus.ENDPTPRIME.HasBits(primeBit) {
|
||||
return statusSuccess
|
||||
}
|
||||
// wait to safely transmit DTD
|
||||
for {
|
||||
dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_ATDTW)
|
||||
_ = dc.bus.ENDPTSTAT.Get() // read-clear endpoint status register
|
||||
if dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_ATDTW) {
|
||||
break
|
||||
}
|
||||
}
|
||||
dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ATDTW)
|
||||
}
|
||||
|
||||
// if QH is empty or the endpoint is not primed, need to link current DTD head
|
||||
// to the QH. if endpoint is not primed and qhIdle is false, QH is empty.
|
||||
if qhIdle || 0 == epStatus&primeBit {
|
||||
qh.nextDTDPointer = dtdHead
|
||||
qh.dtdToken = 0
|
||||
dc.bus.ENDPTPRIME.Set(primeBit)
|
||||
primeAttempt := 0
|
||||
for !dc.bus.ENDPTSTAT.HasBits(primeBit) {
|
||||
if primeAttempt++; primeAttempt >= configDeviceControllerMaxPrimeAttempts {
|
||||
return statusError
|
||||
}
|
||||
if dc.bus.ENDPTCOMPLETE.HasBits(primeBit) {
|
||||
break
|
||||
}
|
||||
dc.bus.ENDPTPRIME.Set(primeBit)
|
||||
}
|
||||
}
|
||||
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (dc *deviceControl) send(address uint8, buffer []uint8, length uint32) status {
|
||||
return dc.transfer((address&specDescriptorEndpointAddressNumberMsk)|
|
||||
(specDescriptorEndpointAddressDirectionIn), buffer, length)
|
||||
}
|
||||
|
||||
func (dc *deviceControl) receive(address uint8, buffer []uint8, length uint32) status {
|
||||
return dc.transfer((address&specDescriptorEndpointAddressNumberMsk)|
|
||||
(specDescriptorEndpointAddressDirectionOut), buffer, length)
|
||||
}
|
||||
|
||||
func (dc *deviceControl) cancel(address uint8) status {
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (dc *deviceControl) control(command deviceControlID, param interface{}) (s status) {
|
||||
|
||||
// assume success unless error condition deliberately detected
|
||||
s = statusSuccess
|
||||
|
||||
switch command {
|
||||
case deviceControlRun:
|
||||
dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS)
|
||||
|
||||
case deviceControlStop:
|
||||
dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_RS)
|
||||
|
||||
case deviceControlEndpointInit:
|
||||
config, ok := param.(*deviceEndpointConfig)
|
||||
if !ok {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
s = dc.initEndpoint(config)
|
||||
|
||||
case deviceControlEndpointDeinit:
|
||||
address, ok := param.(uint8)
|
||||
if !ok {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
s = dc.deinitEndpoint(address)
|
||||
|
||||
case deviceControlEndpointStall:
|
||||
address, ok := param.(uint8)
|
||||
if !ok {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
s = dc.stallEndpoint(address)
|
||||
|
||||
case deviceControlEndpointUnstall:
|
||||
address, ok := param.(uint8)
|
||||
if !ok {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
s = dc.unstallEndpoint(address)
|
||||
|
||||
case deviceControlGetDeviceStatus:
|
||||
// param should be a pointer to uint16, acting as output parameter.
|
||||
stat, ok := param.(*uint16)
|
||||
if !ok {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
// configDeviceSelfPowered is a configuration constant on iMXRT1062
|
||||
*stat = configDeviceSelfPowered <<
|
||||
specRequestStandardGetStatusDeviceSelfPoweredPos
|
||||
|
||||
case deviceControlGetEndpointStatus:
|
||||
// param should be pointer to deviceEndpointStatus, acting as output
|
||||
// parameter.
|
||||
stat, ok := param.(*deviceEndpointStatus)
|
||||
if !ok {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
endpoint, direction := unpackEndpoint(stat.address)
|
||||
if endpoint >= configDeviceMaxEndpoints {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
mask := uint32(nxp.USB_ENDPTCTRL0_RXS)
|
||||
if specOut != direction {
|
||||
mask = nxp.USB_ENDPTCTRL0_TXS
|
||||
}
|
||||
ctrl := dc.endpointControlRegister(endpoint)
|
||||
if nil == ctrl {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
if ctrl.HasBits(mask) {
|
||||
stat.status = uint16(deviceEndpointStateStalled)
|
||||
} else {
|
||||
stat.status = uint16(deviceEndpointStateIdle)
|
||||
}
|
||||
|
||||
case deviceControlPreSetDeviceAddress:
|
||||
address, ok := param.(uint8)
|
||||
if !ok {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
dc.bus.DEVICEADDR.Set((uint32(address) << nxp.USB_DEVICEADDR_USBADR_Pos) |
|
||||
nxp.USB_DEVICEADDR_USBADRA_Msk)
|
||||
|
||||
case deviceControlSetDeviceAddress:
|
||||
// TODO
|
||||
|
||||
case deviceControlGetSynchFrame:
|
||||
return statusNotSupported
|
||||
|
||||
case deviceControlSetDefaultStatus:
|
||||
for i := uint8(0); i < configDeviceMaxEndpoints; i++ {
|
||||
_ = dc.deinitEndpoint(i | specDescriptorEndpointAddressDirectionIn)
|
||||
_ = dc.deinitEndpoint(i | specDescriptorEndpointAddressDirectionOut)
|
||||
}
|
||||
s = dc.resetState()
|
||||
|
||||
case deviceControlGetSpeed:
|
||||
// param should be a pointer to uint8, acting as output parameter.
|
||||
speed, ok := param.(*uint8)
|
||||
if !ok {
|
||||
return statusInvalidParameter
|
||||
}
|
||||
*speed = dc.speed
|
||||
|
||||
case deviceControlGetOTGStatus:
|
||||
return statusNotSupported
|
||||
|
||||
case deviceControlSetOTGStatus:
|
||||
return statusNotSupported
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (dc *deviceControl) critical(enter bool) status {
|
||||
if enter {
|
||||
// check if critical section already locked
|
||||
if dc.criticalState.Get() != 0 {
|
||||
return statusRetry
|
||||
}
|
||||
// lock critical section
|
||||
dc.criticalState.Set(1)
|
||||
// disable interrupts, storing state in receiver
|
||||
dc.interruptMask = arm.DisableInterrupts()
|
||||
} else {
|
||||
// ensure critical section is locked
|
||||
if dc.criticalState.Get() != 0 {
|
||||
// re-enable interrupts, using state stored in receiver
|
||||
arm.EnableInterrupts(dc.interruptMask)
|
||||
// unlock critical section
|
||||
dc.criticalState.Set(0)
|
||||
}
|
||||
}
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (dc *deviceControl) resetState() status {
|
||||
|
||||
dc.dtdFree = dc.dtd
|
||||
p := dc.dtdFree
|
||||
for i := 1; i < configDeviceControllerMaxDTD; i++ {
|
||||
p.nextDTDPointer = getDTDBuffer(dc.port, i)
|
||||
p = p.nextDTDPointer
|
||||
}
|
||||
p.nextDTDPointer = nil
|
||||
dc.dtdCount = configDeviceControllerMaxDTD
|
||||
|
||||
// no interrupt threshold
|
||||
dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk)
|
||||
|
||||
// disable setup lockout
|
||||
dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk)
|
||||
|
||||
// use little-endianness
|
||||
dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk)
|
||||
|
||||
for i := 0; i < 2*configDeviceMaxEndpoints; i++ {
|
||||
qh := getQHBuffer(dc.port, 0, i)
|
||||
qh.capabilities =
|
||||
deviceControllerCapabilities{
|
||||
maxPacketSize: configDeviceControllerMaxPacketSize,
|
||||
}.pack()
|
||||
qh.endpointStatus =
|
||||
deviceControllerEndpointStatus{
|
||||
isOpened: 0,
|
||||
}.pack()
|
||||
qh.nextDTDPointer = deviceControllerDTDTerminate
|
||||
dc.dtdHead[i] = nil
|
||||
dc.dtdTail[i] = nil
|
||||
}
|
||||
dc.bus.ASYNCLISTADDR.Set(uint32(uintptr(unsafe.Pointer(
|
||||
getQHBuffer(dc.port, 0, 0)))))
|
||||
|
||||
dc.bus.DEVICEADDR.Set(0)
|
||||
|
||||
// enable interrupts: bus enable, bus error, port change detect, bus reset
|
||||
dc.bus.USBINTR.Set(nxp.USB_USBINTR_UE_Msk | nxp.USB_USBINTR_UEE_Msk |
|
||||
nxp.USB_USBINTR_PCE_Msk | nxp.USB_USBINTR_URE_Msk)
|
||||
|
||||
dc.isResetting = false
|
||||
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (dc *deviceControl) reset() {
|
||||
|
||||
println("reset")
|
||||
|
||||
// clear setup flag
|
||||
dc.bus.ENDPTSETUPSTAT.Set(dc.bus.ENDPTSETUPSTAT.Get())
|
||||
// clear endpoint complete flag
|
||||
dc.bus.ENDPTCOMPLETE.Set(dc.bus.ENDPTCOMPLETE.Get())
|
||||
|
||||
// flush any pending transfers
|
||||
for dc.bus.ENDPTPRIME.HasBits(nxp.USB_ENDPTPRIME_PERB_Msk | nxp.USB_ENDPTPRIME_PETB_Msk) {
|
||||
dc.bus.ENDPTFLUSH.Set(nxp.USB_ENDPTFLUSH_FERB_Msk | nxp.USB_ENDPTFLUSH_FETB_Msk)
|
||||
}
|
||||
|
||||
// set receiver flag if port reset bit is set; otherwise, notify device class.
|
||||
if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR_Msk) {
|
||||
dc.isResetting = true
|
||||
} else {
|
||||
// send reset notification to common device
|
||||
dc.device.notify(deviceNotification{code: deviceNotifyBusReset})
|
||||
}
|
||||
}
|
||||
|
||||
func (dc *deviceControl) tokenDone() {
|
||||
println("token done")
|
||||
}
|
||||
|
||||
func (dc *deviceControl) portChange() {
|
||||
// check if port is resetting
|
||||
if !dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_PR) {
|
||||
// not resetting, update bus speed
|
||||
if dc.bus.PORTSC1.HasBits(nxp.USB_PORTSC1_HSP) {
|
||||
dc.speed = specSpeedHigh
|
||||
} else {
|
||||
dc.speed = specSpeedFull
|
||||
}
|
||||
// if reset flag is set, notify device layer reset has finished
|
||||
if dc.isResetting {
|
||||
dc.device.notify(
|
||||
deviceNotification{
|
||||
buffer: nil,
|
||||
length: 0,
|
||||
code: deviceNotifyBusReset,
|
||||
isSetup: false,
|
||||
})
|
||||
dc.isResetting = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dc *deviceControl) frameStart() {
|
||||
println("frame start")
|
||||
}
|
||||
|
||||
func (dc *deviceControl) endpointControlRegister(endpoint uint8) *volatile.Register32 {
|
||||
endpoint &= specDescriptorEndpointAddressNumberMsk
|
||||
if endpoint < configDeviceMaxEndpoints {
|
||||
switch endpoint {
|
||||
case 0:
|
||||
return &dc.bus.ENDPTCTRL0
|
||||
case 1:
|
||||
return &dc.bus.ENDPTCTRL1
|
||||
case 2:
|
||||
return &dc.bus.ENDPTCTRL2
|
||||
case 3:
|
||||
return &dc.bus.ENDPTCTRL3
|
||||
case 4:
|
||||
return &dc.bus.ENDPTCTRL4
|
||||
case 5:
|
||||
return &dc.bus.ENDPTCTRL5
|
||||
case 6:
|
||||
return &dc.bus.ENDPTCTRL6
|
||||
case 7:
|
||||
return &dc.bus.ENDPTCTRL7
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *deviceControl) cancelControlPipe(endpoint, direction uint8) status {
|
||||
index := (endpoint << 1) + direction
|
||||
message := deviceNotification{
|
||||
buffer: nil,
|
||||
length: 0,
|
||||
}
|
||||
|
||||
// get DTD of control pipe
|
||||
currentDTD := (*deviceControllerDTD)(unsafe.Pointer(
|
||||
uintptr(unsafe.Pointer(dc.dtdHead[index])) & deviceControllerDTDPointerMsk))
|
||||
|
||||
for nil != currentDTD {
|
||||
originalBuffer := currentDTD.originalBuffer.unpack()
|
||||
dtdToken := currentDTD.dtdToken.unpack()
|
||||
|
||||
// pass transfer buffer address
|
||||
if nil == message.buffer {
|
||||
message.buffer = *(*[]uint8)(unsafe.Pointer(
|
||||
uintptr((currentDTD.bufferPointerPage[0] & deviceControllerDTDPageMsk) |
|
||||
uint32(originalBuffer.originalBufferOffset))))
|
||||
}
|
||||
if 0 != dtdToken.status&deviceControllerDTDStatusActive {
|
||||
message.length = packU32(deviceCDCACMBufferInvalid32)
|
||||
} else {
|
||||
message.length += originalBuffer.originalBufferLength - uint32(dtdToken.totalBytes)
|
||||
}
|
||||
|
||||
if dc.dtdHead[index] == dc.dtdTail[index] {
|
||||
dc.dtdHead[index] = nil
|
||||
dc.dtdTail[index] = nil
|
||||
qh := getQHBuffer(dc.port, 0, int(index))
|
||||
qh.nextDTDPointer = deviceControllerDTDTerminate
|
||||
qh.dtdToken = 0
|
||||
} else {
|
||||
dc.dtdHead[index] = dc.dtdHead[index].nextDTDPointer
|
||||
}
|
||||
|
||||
if 0 != currentDTD.dtdToken.unpack().ioc ||
|
||||
0 == uintptr(unsafe.Pointer(dc.dtdHead[index]))&deviceControllerDTDPointerMsk {
|
||||
message.code = deviceNotificationID(endpoint |
|
||||
(direction << specDescriptorEndpointAddressDirectionPos))
|
||||
message.isSetup = false
|
||||
dc.device.notify(message)
|
||||
message.buffer = nil
|
||||
message.length = 0
|
||||
}
|
||||
|
||||
currentDTD.dtdToken = 0
|
||||
currentDTD.nextDTDPointer = dc.dtdFree
|
||||
dc.dtdFree = currentDTD
|
||||
dc.dtdCount++
|
||||
|
||||
// get DTD of control pipe
|
||||
currentDTD = (*deviceControllerDTD)(unsafe.Pointer(
|
||||
uintptr(unsafe.Pointer(dc.dtdHead[index])) & deviceControllerDTDPointerMsk))
|
||||
|
||||
}
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (dc *deviceControl) initEndpoint(config *deviceEndpointConfig) status {
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (dc *deviceControl) deinitEndpoint(address uint8) status {
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (dc *deviceControl) stallEndpoint(address uint8) status {
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (dc *deviceControl) unstallEndpoint(address uint8) status {
|
||||
return statusSuccess
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package usb2
|
||||
package usb
|
||||
|
||||
type hcd interface {
|
||||
class() class
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb2
|
||||
package usb
|
||||
|
||||
// Implementation of USB host controller driver (hcd) for NXP iMXRT1062.
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package usb
|
||||
|
||||
// Unexported USB device type definitions.
|
||||
// In device mode, a USB device represents the whole USB port controller.
|
||||
// In host mode, a USB device represents a peripheral device connected to the
|
||||
// USB port controller.
|
||||
type (
|
||||
host struct {
|
||||
port uint8
|
||||
}
|
||||
)
|
||||
|
||||
func (h *host) init(port uint8) (s status) {
|
||||
|
||||
// initialize host
|
||||
h.port = port
|
||||
|
||||
return statusSuccess
|
||||
}
|
||||
|
||||
func (h *host) deinit() status {
|
||||
|
||||
// de-initialize host
|
||||
return statusSuccess
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package usb
|
||||
|
||||
// hostController provides hardware abstraction over a platform's physical
|
||||
// USB host port controller (e.g., an EHCI-compliant peripheral).
|
||||
//
|
||||
// To connect the USB 2.0 host driver stack to a particular platform, the
|
||||
// following method must be implemented:
|
||||
//
|
||||
// func (h *host) initController() hostController
|
||||
//
|
||||
// This method shall return an implementation of hostController, or nil if a
|
||||
// controller cannot be allocated for the given port.
|
||||
type hostController interface{}
|
||||
@@ -1,167 +0,0 @@
|
||||
package usb
|
||||
|
||||
// Constants defined per USB 2.0 specification.
|
||||
const (
|
||||
// USB speed
|
||||
specSpeedFull = 0x00
|
||||
specSpeedLow = 0x01
|
||||
specSpeedHigh = 0x02
|
||||
specSpeedSuper = 0x04
|
||||
|
||||
// USB standard descriptor endpoint type
|
||||
specEndpointControl = 0x00
|
||||
specEndpointIsochronous = 0x01
|
||||
specEndpointBulk = 0x02
|
||||
specEndpointInterrupt = 0x03
|
||||
|
||||
// USB standard descriptor transfer direction
|
||||
specOut = 0
|
||||
specIn = 1
|
||||
|
||||
// USB standard descriptor length
|
||||
specDescriptorLengthDevice = 18
|
||||
specDescriptorLengthConfigure = 9
|
||||
specDescriptorLengthInterface = 9
|
||||
specDescriptorLengthEndpoint = 7
|
||||
specDescriptorLengthEndpointCompanion = 6
|
||||
specDescriptorLengthDeviceQualitier = 10
|
||||
specDescriptorLengthOTGDescriptor = 5
|
||||
specDescriptorLengthBOSDescriptor = 5
|
||||
specDescriptorLengthDeviceCapabilityUSB20Extension = 7
|
||||
specDescriptorLengthDeviceCapabilitySuperspeed = 10
|
||||
|
||||
// USB device capability type codes
|
||||
specDescriptorTypeDeviceCapabilityWireless = 0x01
|
||||
specDescriptorTypeDeviceCapabilityUSB20Extension = 0x02
|
||||
specDescriptorTypeDeviceCapabilitySuperspeed = 0x03
|
||||
|
||||
// USB standard descriptor type
|
||||
specDescriptorTypeDevice = 0x01
|
||||
specDescriptorTypeConfigure = 0x02
|
||||
specDescriptorTypeString = 0x03
|
||||
specDescriptorTypeInterface = 0x04
|
||||
specDescriptorTypeEndpoint = 0x05
|
||||
specDescriptorTypeDeviceQualitier = 0x06
|
||||
specDescriptorTypeOtherSpeedConfiguration = 0x07
|
||||
specDescriptorTypeInterfaacePower = 0x08
|
||||
specDescriptorTypeOTG = 0x09
|
||||
specDescriptorTypeInterfaceAssociation = 0x0B
|
||||
specDescriptorTypeBOS = 0x0F
|
||||
specDescriptorTypeDeviceCapability = 0x10
|
||||
|
||||
specDescriptorTypeHID = 0x21
|
||||
specDescriptorTypeHIDReport = 0x22
|
||||
specDescriptorTypeHIDPhysical = 0x23
|
||||
|
||||
specDescriptorTypeCDCInterface = 0x24
|
||||
specDescriptorTypeCDCEndpoint = 0x25
|
||||
|
||||
specDescriptorTypeEndpointCompanion = 0x30
|
||||
|
||||
// USB standard request type
|
||||
specRequestTypeDirMsk = 0x80
|
||||
specRequestTypeDirPos = 7
|
||||
specRequestTypeDirOut = 0x00
|
||||
specRequestTypeDirIn = 0x80
|
||||
|
||||
specRequestTypeTypeMsk = 0x60
|
||||
specRequestTypeTypePos = 5
|
||||
specRequestTypeTypeStandard = 0
|
||||
specRequestTypeTypeClass = 0x20
|
||||
specRequestTypeTypeVendor = 0x40
|
||||
|
||||
specRequestTypeRecipientMsk = 0x1F
|
||||
specRequestTypeRecipientPos = 0
|
||||
specRequestTypeRecipientDevice = 0x00
|
||||
specRequestTypeRecipientInterface = 0x01
|
||||
specRequestTypeRecipientEndpoint = 0x02
|
||||
specRequestTypeRecipientOther = 0x03
|
||||
|
||||
// USB standard request
|
||||
specRequestStandardGetStatus = 0x00
|
||||
specRequestStandardClearFeature = 0x01
|
||||
specRequestStandardSetFeature = 0x03
|
||||
specRequestStandardSetAddress = 0x05
|
||||
specRequestStandardGetDescriptor = 0x06
|
||||
specRequestStandardSetDescriptor = 0x07
|
||||
specRequestStandardGetConfiguration = 0x08
|
||||
specRequestStandardSetConfiguration = 0x09
|
||||
specRequestStandardGetInterface = 0x0A
|
||||
specRequestStandardSetInterface = 0x0B
|
||||
specRequestStandardSynchFrame = 0x0C
|
||||
|
||||
// USB standard request: GET status
|
||||
specRequestStandardGetStatusDeviceSelfPoweredPos = 0
|
||||
specRequestStandardGetStatusDeviceRemoteWakeupPos = 1
|
||||
|
||||
specRequestStandardGetStatusEndpointHaltMsk = 0x01
|
||||
specRequestStandardGetStatusEndpointHaltPos = 0
|
||||
|
||||
specRequestStandardGetStatusOTGStatusSelector = 0xF000
|
||||
|
||||
// USB standard request: CLEAR/SET feature
|
||||
specRequestStandardFeatureSelectorEndpointHalt = 0
|
||||
specRequestStandardFeatureSelectorDeviceRemoteWakeup = 1
|
||||
specRequestStandardFeatureSelectorDeviceTestMode = 2
|
||||
specRequestStandardFeatureSelectorBHNPEnable = 3
|
||||
specRequestStandardFeatureSelectorAHNPSupport = 4
|
||||
specRequestStandardFeatureSelectorAAltHNPSupport = 5
|
||||
|
||||
// USB standard descriptor: configure attributes
|
||||
specDescriptorConfigureAttributeD7Msk = 0x80
|
||||
specDescriptorConfigureAttributeD7Pos = 7
|
||||
|
||||
specDescriptorConfigureAttributeSelfPoweredMsk = 0x40
|
||||
specDescriptorConfigureAttributeSelfPoweredPos = 6
|
||||
|
||||
specDescriptorConfigureAttributeRemoteWakeupMsk = 0x20
|
||||
specDescriptorConfigureAttributeRemoteWakeupPos = 5
|
||||
|
||||
// USB standard descriptor: endpoint attributes
|
||||
specDescriptorEndpointAddressDirectionMsk = 0x80
|
||||
specDescriptorEndpointAddressDirectionPos = 7
|
||||
specDescriptorEndpointAddressDirectionOut = 0
|
||||
specDescriptorEndpointAddressDirectionIn = 0x80
|
||||
|
||||
specDescriptorEndpointAddressNumberMsk = 0x0F
|
||||
specDescriptorEndpointAddressNumberPos = 0
|
||||
|
||||
specDescriptorEndpointAttributeTypeMsk = 0x03
|
||||
specDescriptorEndpointAttributeNumberPos = 0
|
||||
|
||||
specDescriptorEndpointAttributeSyncTypeMsk = 0x0C
|
||||
specDescriptorEndpointAttributeSyncTypePos = 2
|
||||
specDescriptorEndpointAttributeSyncTypeNoSync = 0x00
|
||||
specDescriptorEndpointAttributeSyncTypeAsync = 0x04
|
||||
specDescriptorEndpointAttributeSyncTypeAdaptive = 0x08
|
||||
specDescriptorEndpointAttributeSyncTypeSync = 0x0C
|
||||
|
||||
specDescriptorEndpointAttributeUsageTypeMsk = 0x30
|
||||
specDescriptorEndpointAttributeUsageTypePos = 4
|
||||
specDescriptorEndpointAttributeUsageTypeDataEndpoint = 0x00
|
||||
specDescriptorEndpointAttributeUsageTypeFeedbackEndpoint = 0x10
|
||||
specDescriptorEndpointAttributeUsageTypeImplicitFeedbackDataEndpoint = 0x20
|
||||
|
||||
specDescriptorEndpointMaxpacketsizeSizeMsk = 0x07FF
|
||||
specDescriptorEndpointMaxpacketsizeMultTransactionsMsk = 0x1800
|
||||
specDescriptorEndpointMaxpacketsizeMultTransactionsPos = 11
|
||||
|
||||
// USB standard descriptor: OTG attributes
|
||||
specDescriptorOTGAttributesSRPMsk = 0x01
|
||||
specDescriptorOTGAttributesHNPMsk = 0x02
|
||||
specDescriptorOTGAttributesADPMsk = 0x04
|
||||
|
||||
// USB standard descriptor: device capability attributes (USB 2.0 extension)
|
||||
specDescriptorDeviceCapabilityUSB20ExtensionLPMMsk = 0x02
|
||||
specDescriptorDeviceCapabilityUSB20ExtensionLPMPos = 1
|
||||
specDescriptorDeviceCapabilityUSB20ExtensionBESLMsk = 0x04
|
||||
specDescriptorDeviceCapabilityUSB20ExtensionBESLPos = 2
|
||||
)
|
||||
|
||||
//go:inline
|
||||
func unpackEndpoint(address uint8) (number, direction uint8) {
|
||||
return (address & specDescriptorEndpointAddressNumberMsk) >>
|
||||
specDescriptorEndpointAddressNumberPos,
|
||||
(address & specDescriptorEndpointAddressDirectionMsk) >>
|
||||
specDescriptorEndpointAddressDirectionPos
|
||||
}
|
||||
+58
-331
@@ -5,7 +5,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidPort = errors.New("invalid USB port")
|
||||
ErrUARTInvalidPort = errors.New("invalid USB port")
|
||||
ErrUARTInvalidCore = errors.New("invalid USB core")
|
||||
ErrUARTEmptyBuffer = errors.New("USB receive buffer empty")
|
||||
ErrUARTWriteFailed = errors.New("USB write failure")
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -16,351 +19,75 @@ type (
|
||||
// UART represents a virtual serial (UART) device emulation using the USB
|
||||
// CDC-ACM device class driver.
|
||||
UART struct {
|
||||
port uint8 // USB port (core index, e.g., 0-1)
|
||||
desc *ConfigDeviceDescriptor // User-provided USB device descriptor information
|
||||
class *deviceClass // USB device class handle
|
||||
acm *deviceCDCACM // USB CDC-ACM device class handle
|
||||
|
||||
attached bool
|
||||
transact bool
|
||||
config uint8 // selected configuration index (1-based, 0=invalid)
|
||||
speed uint8 // enumerated bus speed (low, full, high)
|
||||
alternate deviceCDCACMAlternateList
|
||||
port int // USB port (core index, e.g., 0-1)
|
||||
core *core
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
uartAbstractState = []uint8{0x00, 0x00}
|
||||
uartCountryCode = []uint8{0x00, 0x00}
|
||||
)
|
||||
|
||||
func (uart *UART) SetPort(port uint8) {
|
||||
if port >= ConfigPortCount || port >= configDeviceCount ||
|
||||
port >= configDeviceCDCACMCount {
|
||||
return // invalid port for USB CDC-ACM device class
|
||||
}
|
||||
// TODO: if changed, may need to reset port controller and tell host to
|
||||
// re-enumerate devices
|
||||
uart.port = port
|
||||
}
|
||||
|
||||
func (uart *UART) SetDeviceDescriptor(desc *ConfigDeviceDescriptor) {
|
||||
if nil == desc {
|
||||
return // invalid device descriptor information
|
||||
}
|
||||
// TODO: if changed, may need to reset port controller and tell host to
|
||||
// re-enumerate devices
|
||||
uart.desc = desc
|
||||
}
|
||||
|
||||
func (uart *UART) Configure(config UARTConfig) error {
|
||||
|
||||
if uart.port >= ConfigPortCount || uart.port >= configDeviceCount ||
|
||||
uart.port >= configDeviceCDCACMCount {
|
||||
return ErrInvalidPort
|
||||
}
|
||||
|
||||
// use default configuration index (1-based index; 0=invalid)
|
||||
uart.config = configDeviceCDCACMConfigurationIndex
|
||||
|
||||
// modify the global basic configuration struct configDeviceCDCACM for our USB
|
||||
// port and configuration index.
|
||||
//
|
||||
// these settings are copied into the real CDC-ACM object, using interface
|
||||
// deviceClassDriver, via initialization method (*deviceCDCACM).init().
|
||||
|
||||
// change baud rate from default, if provided
|
||||
if config.BaudRate != 0 {
|
||||
configDeviceCDCACM[uart.port][uart.config-1].lineCodingBaudRate =
|
||||
config.BaudRate
|
||||
if uart.port >= CoreCount || uart.port >= dcdCount {
|
||||
return ErrUARTInvalidPort
|
||||
}
|
||||
|
||||
// verify we have a free USB port and take ownership of it
|
||||
port, status := initPort(uart.port, modeDevice)
|
||||
if !status.OK() {
|
||||
return status
|
||||
var st status
|
||||
uart.core, st = initCore(uart.port, class{id: classDeviceCDCACM, config: 1})
|
||||
if !st.ok() {
|
||||
return ErrUARTInvalidPort
|
||||
}
|
||||
|
||||
// apply the CDC-ACM configuration to our port
|
||||
uart.acm, uart.class = port.initCDCACM(uart.config, uart)
|
||||
|
||||
// enable USB device mode functionality, which allows a host to enumerate us
|
||||
status = port.device.controller.enable(true)
|
||||
if !status.OK() {
|
||||
return status
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uart *UART) deviceEvent(ev deviceEventID, param interface{}) status {
|
||||
|
||||
switch ev {
|
||||
case deviceEventBusReset:
|
||||
uart.attached = false
|
||||
uart.config = 0 // clear selected configuration (1-based index)
|
||||
s := uart.acm.device.busSpeed(&uart.speed)
|
||||
if s.OK() {
|
||||
s = uart.acm.device.setBusSpeed(uart.speed)
|
||||
}
|
||||
return s
|
||||
|
||||
case deviceEventSetConfiguration:
|
||||
if id, ok := param.(uint8); ok {
|
||||
if 0 == id {
|
||||
uart.attached = false
|
||||
uart.config = 0 // clear selected configuration (1-based index)
|
||||
return statusSuccess
|
||||
}
|
||||
// verify we have a config struct at given index
|
||||
if nil != uart.class && nil != uart.class.config &&
|
||||
int(id) <= len(uart.class.config) &&
|
||||
int(id) <= len(configDeviceCDCACM[uart.port]) {
|
||||
config := uart.class.config[id-1] // receiver configuration
|
||||
system := configDeviceCDCACM[uart.port][id-1] // platform configuration
|
||||
// verify the config is a CDC device and has a defined driver
|
||||
if deviceClassCDC == config.info.classID && nil != config.driver {
|
||||
// finally, verify the config driver is an ACM device driver
|
||||
if _, ok := config.driver.(*deviceCDCACM); ok {
|
||||
uart.attached = true
|
||||
uart.config = id
|
||||
return uart.acm.receive(system.dataBulkOutEndpoint, uart.acm.recvBuffer[:],
|
||||
uint32(system.dataBulkOutPacketSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return statusNotSupported // all non-error paths return early
|
||||
|
||||
case deviceEventSetInterface:
|
||||
if uart.attached {
|
||||
if setting, ok := param.(uint16); ok {
|
||||
inf := (setting & 0xFF00) >> 8
|
||||
if inf < configDeviceCDCACMInterfaceCount {
|
||||
uart.alternate[inf] = setting & 0x00FF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case deviceEventGetConfiguration:
|
||||
case deviceEventGetInterface:
|
||||
case deviceEventGetDeviceDescriptor:
|
||||
case deviceEventGetConfigurationDescriptor:
|
||||
case deviceEventGetStringDescriptor:
|
||||
// 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 statusSuccess
|
||||
return dc.uartAvailable()
|
||||
}
|
||||
|
||||
func (uart *UART) classEvent(ev uint32, param interface{}) status {
|
||||
|
||||
if nil == uart.acm {
|
||||
return statusInvalidHandle
|
||||
// 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
|
||||
}
|
||||
|
||||
switch deviceCDCACMEventID(ev) {
|
||||
case deviceCDCACMEventSendResponse:
|
||||
if ctl, ok := param.(deviceEndpointControlMessage); ok {
|
||||
|
||||
// verify receiver configuration
|
||||
if int(uart.port) >= len(configDeviceCDCACM) || uart.config == 0 ||
|
||||
int(uart.config) > len(configDeviceCDCACM[uart.port]) {
|
||||
return statusNotSupported
|
||||
}
|
||||
// get platform configuration
|
||||
system := configDeviceCDCACM[uart.port][uart.config-1]
|
||||
|
||||
if ctl.length > 0 && 0 != ctl.length%uint32(system.dataBulkInPacketSize) {
|
||||
// If the last packet is the size of endpoint, then send an additional
|
||||
// zero-ended packet to notify the host it may flush output.
|
||||
return uart.acm.send(system.dataBulkInEndpoint, nil, 0)
|
||||
}
|
||||
|
||||
if uart.attached && uart.transact &&
|
||||
(nil != ctl.buffer || (nil == ctl.buffer && 0 == ctl.length)) {
|
||||
// send complete, schedule buffer for next receive event
|
||||
return uart.acm.receive(system.dataBulkOutEndpoint, uart.acm.recvBuffer[:],
|
||||
uint32(system.dataBulkOutPacketSize))
|
||||
}
|
||||
}
|
||||
return statusError // all non-error paths return early
|
||||
|
||||
case deviceCDCACMEventRecvResponse:
|
||||
if ctl, ok := param.(deviceEndpointControlMessage); ok {
|
||||
if uart.attached && uart.transact {
|
||||
uart.acm.recvSize = ctl.length
|
||||
if 0 == ctl.length {
|
||||
|
||||
// verify receiver configuration
|
||||
if int(uart.port) >= len(configDeviceCDCACM) || uart.config == 0 ||
|
||||
int(uart.config) > len(configDeviceCDCACM[uart.port]) {
|
||||
return statusNotSupported
|
||||
}
|
||||
// get platform configuration
|
||||
system := configDeviceCDCACM[uart.port][uart.config-1]
|
||||
|
||||
// schedule buffer for next receive event
|
||||
return uart.acm.receive(system.dataBulkOutEndpoint, uart.acm.recvBuffer[:],
|
||||
uint32(system.dataBulkOutPacketSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
return statusError // all non-error paths return early
|
||||
|
||||
case deviceCDCACMEventSerialStateNotify:
|
||||
uart.acm.hasSentState = false
|
||||
return statusSuccess
|
||||
|
||||
case deviceCDCACMEventSendEncapsulatedCommand:
|
||||
return statusNotImplemented
|
||||
|
||||
case deviceCDCACMEventGetEncapsulatedResponse:
|
||||
return statusNotImplemented
|
||||
|
||||
case deviceCDCACMEventSetCommFeature:
|
||||
if req, ok := param.(deviceCDCACMRequestParam); ok {
|
||||
switch req.setupValue {
|
||||
case deviceCDCFeatureAbstractState:
|
||||
if req.isSetup {
|
||||
*(req.buffer) = uartAbstractState
|
||||
} else {
|
||||
*(req.length) = 0
|
||||
}
|
||||
return statusSuccess
|
||||
|
||||
case deviceCDCFeatureCountrySetting:
|
||||
if req.isSetup {
|
||||
*(req.buffer) = uartCountryCode
|
||||
} else {
|
||||
*(req.length) = 0
|
||||
}
|
||||
return statusSuccess
|
||||
}
|
||||
return statusInvalidParameter // unrecognized request code
|
||||
}
|
||||
return statusError // all non-error paths return early
|
||||
|
||||
case deviceCDCACMEventGetCommFeature:
|
||||
if req, ok := param.(deviceCDCACMRequestParam); ok {
|
||||
switch req.setupValue {
|
||||
case deviceCDCFeatureAbstractState:
|
||||
*(req.buffer) = uartAbstractState
|
||||
*(req.length) = uint32(len(uartAbstractState))
|
||||
return statusSuccess
|
||||
|
||||
case deviceCDCFeatureCountrySetting:
|
||||
*(req.buffer) = uartCountryCode
|
||||
*(req.length) = uint32(len(uartCountryCode))
|
||||
return statusSuccess
|
||||
}
|
||||
return statusInvalidParameter // unrecognized request code
|
||||
}
|
||||
return statusError // all non-error paths return early
|
||||
|
||||
case deviceCDCACMEventClearCommFeature:
|
||||
return statusNotImplemented
|
||||
|
||||
case deviceCDCACMEventGetLineCoding:
|
||||
if req, ok := param.(deviceCDCACMRequestParam); ok {
|
||||
// verify receiver configuration
|
||||
if int(uart.port) >= len(deviceCDCACMLineCoding) || uart.config == 0 ||
|
||||
int(uart.config) > len(deviceCDCACMLineCoding[uart.port]) {
|
||||
return statusNotSupported
|
||||
}
|
||||
lineCoding := deviceCDCACMLineCoding[uart.port][uart.config-1]
|
||||
*(req.buffer) = lineCoding
|
||||
*(req.length) = uint32(len(lineCoding))
|
||||
return statusSuccess
|
||||
}
|
||||
return statusError // all non-error paths return early
|
||||
|
||||
case deviceCDCACMEventSetLineCoding:
|
||||
if req, ok := param.(deviceCDCACMRequestParam); ok {
|
||||
// verify receiver configuration
|
||||
if int(uart.port) >= len(deviceCDCACMLineCoding) || uart.config == 0 ||
|
||||
int(uart.config) > len(deviceCDCACMLineCoding[uart.port]) {
|
||||
return statusNotSupported
|
||||
}
|
||||
lineCoding := deviceCDCACMLineCoding[uart.port][uart.config-1]
|
||||
if req.isSetup {
|
||||
*(req.buffer) = lineCoding
|
||||
} else {
|
||||
*(req.length) = uint32(len(lineCoding))
|
||||
}
|
||||
return statusSuccess
|
||||
}
|
||||
return statusError // all non-error paths return early
|
||||
|
||||
case deviceCDCACMEventSetControlLineState:
|
||||
if req, ok := param.(deviceCDCACMRequestParam); ok {
|
||||
// verify receiver configuration
|
||||
if int(uart.port) >= len(configDeviceCDCACM) || uart.config == 0 ||
|
||||
int(uart.config) > len(configDeviceCDCACM[uart.port]) {
|
||||
return statusNotSupported
|
||||
}
|
||||
// get platform configuration
|
||||
system := configDeviceCDCACM[uart.port][uart.config-1]
|
||||
|
||||
uart.acm.info.dteStatus = uint8(req.setupValue)
|
||||
|
||||
// activate/deactivate Tx carrier
|
||||
if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapCarrierActivation {
|
||||
uart.acm.info.uartState |= deviceCDCUARTStateTxCarrier
|
||||
} else {
|
||||
uart.acm.info.uartState &^= deviceCDCUARTStateTxCarrier
|
||||
}
|
||||
|
||||
// activate/deactivate DTE and carrier
|
||||
if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapDTEPresence {
|
||||
uart.acm.info.uartState |= deviceCDCUARTStateRxCarrier
|
||||
uart.acm.info.dtePresent = true // serial device is now open on host
|
||||
} else {
|
||||
uart.acm.info.uartState &^= deviceCDCUARTStateRxCarrier
|
||||
uart.acm.info.dtePresent = false // serial device is now closed on host
|
||||
}
|
||||
|
||||
uart.acm.info.serialState[0] = deviceCDCACMRequestNotify // bmRequestType
|
||||
uart.acm.info.serialState[1] = deviceCDCNotifySerialState // bNotification
|
||||
uart.acm.info.serialState[2] = 0 // wValue (lo)
|
||||
uart.acm.info.serialState[3] = 0 // wValue (hi)
|
||||
uart.acm.info.serialState[4] = uint8(req.interfaceIndex) // wIndex (lo)
|
||||
uart.acm.info.serialState[5] = uint8(req.interfaceIndex >> 8) // wIndex (hi)
|
||||
uart.acm.info.serialState[6] = deviceCDCACMInfoUARTBitmapSize // wLength (lo)
|
||||
uart.acm.info.serialState[7] = 0 // wLength (hi)
|
||||
uart.acm.info.serialState[8] = uint8(uart.acm.info.uartState) // UART bitmap (lo)
|
||||
uart.acm.info.serialState[9] = uint8(uart.acm.info.uartState >> 8) // UART bitmap (hi)
|
||||
|
||||
s := statusSuccess
|
||||
if !uart.acm.hasSentState {
|
||||
s = uart.acm.send(system.commInterruptInEndpoint,
|
||||
uart.acm.info.serialState[:], deviceCDCACMSerialStateSize)
|
||||
uart.acm.hasSentState = true
|
||||
}
|
||||
|
||||
// update status
|
||||
if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapCarrierActivation {
|
||||
// carrier activated
|
||||
}
|
||||
if 0 != uart.acm.info.dteStatus&deviceCDCControlSigBitmapDTEPresence {
|
||||
// DTE activated
|
||||
if uart.attached {
|
||||
uart.transact = true
|
||||
}
|
||||
} else {
|
||||
// DTE deactivated
|
||||
if uart.attached {
|
||||
uart.transact = false
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
return statusError // all non-error paths return early
|
||||
|
||||
case deviceCDCACMEventSendBreak:
|
||||
return statusNotImplemented
|
||||
|
||||
default:
|
||||
return statusInvalidParameter
|
||||
n, ok := dc.uartReadByte()
|
||||
if !ok {
|
||||
return 0, ErrUARTEmptyBuffer
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Read from the RX buffer.
|
||||
func (uart UART) Read(data []byte) (n int, err error) {
|
||||
dc, ok := uart.core.dc.(*deviceController)
|
||||
if !ok {
|
||||
return 0, ErrUARTInvalidCore
|
||||
}
|
||||
return dc.uartRead(data), nil
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte of data to the UART interface.
|
||||
func (uart UART) WriteByte(c byte) error {
|
||||
dc, ok := uart.core.dc.(*deviceController)
|
||||
if !ok {
|
||||
return ErrUARTInvalidCore
|
||||
}
|
||||
if !dc.uartWriteByte(c) {
|
||||
return ErrUARTWriteFailed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write data to the UART.
|
||||
func (uart UART) Write(data []byte) (n int, err error) {
|
||||
dc, ok := uart.core.dc.(*deviceController)
|
||||
if !ok {
|
||||
return 0, ErrUARTInvalidCore
|
||||
}
|
||||
return dc.uartWrite(data), nil
|
||||
}
|
||||
|
||||
+138
-397
@@ -1,416 +1,157 @@
|
||||
package usb
|
||||
|
||||
type (
|
||||
// status is the common error code used internally for package operations.
|
||||
status int8
|
||||
// mode defines the operating mode of a USB port.
|
||||
mode int8
|
||||
// Hardware abstraction for USB ports configured as either host or device.
|
||||
|
||||
// port represents a physical USB port, which may be configured as either a
|
||||
// device or as a host.
|
||||
port struct {
|
||||
mode mode
|
||||
device device
|
||||
host host
|
||||
}
|
||||
)
|
||||
|
||||
// Constants for unexported types shared across entire package.
|
||||
const (
|
||||
statusSuccess status = iota // Success
|
||||
statusError // Failed
|
||||
statusBusy // Busy
|
||||
statusInvalidHandle // Invalid handle
|
||||
statusInvalidParameter // Invalid parameter
|
||||
statusInvalidRequest // Invalid request
|
||||
statusControllerNotFound // Controller cannot be found
|
||||
statusInvalidController // Invalid controller interface
|
||||
statusNotSupported // Configuration is not supported
|
||||
statusRetry // Enumeration get configuration retry
|
||||
statusTransferStall // Transfer stalled
|
||||
statusTransferFailed // Transfer failed
|
||||
statusAllocFail // Allocation failed
|
||||
statusLackSwapBuffer // Insufficient swap buffer for KHCI
|
||||
statusTransferCancel // The transfer cancelled
|
||||
statusBandwidthFail // Allocate bandwidth failed
|
||||
statusMSDStatusFail // For MSD, the CSW status means fail
|
||||
statusEHCIAttached // EHCI attached
|
||||
statusEHCIDetached // EHCI detached
|
||||
statusDataOverRun // Endpoint data (Rx) exceeds max size
|
||||
statusNotImplemented // Supported feature not implemented
|
||||
|
||||
modeIdle mode = iota // USB core idle (unallocated)
|
||||
modeDevice // USB device mode
|
||||
modeHost // USB host mode
|
||||
)
|
||||
|
||||
var (
|
||||
// portInstance holds instances for all available ports on the platform.
|
||||
portInstance [ConfigPortCount]port
|
||||
)
|
||||
import "unsafe"
|
||||
|
||||
func init() {
|
||||
// ensure all ports are in idle state by default
|
||||
for i := range portInstance {
|
||||
portInstance[i].mode = modeIdle
|
||||
if unsafe.Sizeof(uintptr(0)) > 4 {
|
||||
panic("USB is only supported on 32-bit systems")
|
||||
}
|
||||
}
|
||||
|
||||
func ProcessMessages() (err error) {
|
||||
for i := range portInstance {
|
||||
s := portInstance[i].process()
|
||||
if !s.OK() && nil == err {
|
||||
err = s
|
||||
// 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
|
||||
|
||||
// coreInstance provides statically-allocated instances of each USB core
|
||||
// configured on this platform.
|
||||
var coreInstance [CoreCount]core
|
||||
|
||||
// status represents the return code of a subroutine.
|
||||
type status uint8
|
||||
|
||||
// Constant definitions for all status codes used within the package.
|
||||
const (
|
||||
statusOK status = iota // Success
|
||||
statusBusy // Busy
|
||||
statusRetry // Retry
|
||||
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
|
||||
// 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) {
|
||||
|
||||
if port < 0 || port >= CoreCount || 0 == class.config {
|
||||
return nil, statusInvalid
|
||||
}
|
||||
|
||||
if modeIdle != coreInstance[port].mode {
|
||||
// Check if requested port is already configured as requested class. If so,
|
||||
// just return a reference to the existing core instead of an error.
|
||||
// For instance, this will allow TinyGo examples that try to reconfigure the
|
||||
// USB (CDC-ACM) UART port (which is already configured by the runtime) to
|
||||
// continue without error.
|
||||
if coreInstance[port].mode == class.mode() {
|
||||
switch class.mode() {
|
||||
case modeDevice:
|
||||
if coreInstance[port].dc.class().equals(class) {
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
case modeHost:
|
||||
if coreInstance[port].hc.class().equals(class) {
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// initPort configures the mode for a given USB port and initializes the
|
||||
// hardware's port controller. If the port is invalid or not idle (it has
|
||||
// already been configured), it returns nil and a status code.
|
||||
func initPort(port uint8, mode mode) (*port, status) {
|
||||
if port >= ConfigPortCount || int(port) >= len(portInstance) {
|
||||
return nil, statusInvalidController
|
||||
}
|
||||
if modeIdle != portInstance[port].mode {
|
||||
return nil, statusBusy
|
||||
}
|
||||
portInstance[port].mode = mode
|
||||
switch mode {
|
||||
|
||||
switch class.mode() {
|
||||
case modeDevice:
|
||||
if s := portInstance[port].device.init(port); !s.OK() {
|
||||
return nil, s
|
||||
// Allocate a free device controller and install interrupts
|
||||
dc, st := initDCD(port, class)
|
||||
if !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
case modeHost:
|
||||
if s := portInstance[port].host.init(port); !s.OK() {
|
||||
return nil, s
|
||||
// Initialize buffers and device descriptors
|
||||
if st = dc.init(); !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
return &portInstance[port], statusSuccess
|
||||
}
|
||||
|
||||
// deinit disables the receiver USB port, changing its mode to idle, freeing it
|
||||
// for reuse or reconfiguration.
|
||||
func (p *port) deinit() status {
|
||||
switch p.mode {
|
||||
case modeDevice:
|
||||
return p.device.deinit()
|
||||
case modeHost:
|
||||
return p.host.deinit()
|
||||
// Allocate a free host controller and install interrupts
|
||||
hc, st := initHCD(port, class)
|
||||
if !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
// Initialize buffers and device descriptors
|
||||
if st = hc.init(); !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
coreInstance[port].port = port
|
||||
coreInstance[port].mode = 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
|
||||
}
|
||||
|
||||
default:
|
||||
return statusInvalidController
|
||||
return nil, statusInvalid
|
||||
}
|
||||
}
|
||||
|
||||
func (p *port) process() status {
|
||||
switch p.mode {
|
||||
case modeDevice:
|
||||
return p.device.controller.process()
|
||||
case modeHost:
|
||||
return statusSuccess // TODO: not implemented
|
||||
default:
|
||||
return statusInvalidController
|
||||
}
|
||||
}
|
||||
|
||||
// initCDCACM applies a CDC-ACM configuration to the receiver port p and then
|
||||
// returns the configured deviceClassDriver and deviceClass that were assigned
|
||||
// to the receiver.
|
||||
//
|
||||
// The given deviceClassEventHandler is called for any USB device-level event
|
||||
// notifications received, which allows an upper-layer CDC-ACM driver (such as
|
||||
// a UART interface implementation) the opportunity to handle device events.
|
||||
func (p *port) initCDCACM(id uint8, handler deviceClassEventHandler) (*deviceCDCACM, *deviceClass) {
|
||||
|
||||
// verify a valid port was provided
|
||||
if nil == p || nil == p.device.controller || p.mode != modeDevice {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if 0 == id || int(id) > len(configDeviceCDCACM[p.device.port]) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// get a reference to each of the class interfaces
|
||||
comm := &deviceCDCACMConfigInstance[p.device.port][id-1].info.interfaceList[0]
|
||||
data := &deviceCDCACMConfigInstance[p.device.port][id-1].info.interfaceList[1]
|
||||
|
||||
// configDeviceCDCACM must be defined per package API. these settings will be
|
||||
// platform-specific, and will probably be implemented in a build tag-
|
||||
// constrained source file. the length of this array corresponds to the number
|
||||
// of USB CDC-ACM ports that are being created, and the index of each element
|
||||
// corresponds to the physical USB port (core index). Each element is a slice
|
||||
// of alternate device configurations that may be selected for a given port.
|
||||
|
||||
// CDC-ACM Communication/control interface
|
||||
comm.interfaceNumber =
|
||||
configDeviceCDCACM[p.device.port][id-1].commInterfaceIndex
|
||||
|
||||
comm.deviceInterface[0].endpoint[0].address =
|
||||
configDeviceCDCACM[p.device.port][id-1].commInterruptInEndpoint |
|
||||
specDescriptorEndpointAddressDirectionIn
|
||||
|
||||
comm.deviceInterface[0].endpoint[0].maxPacketSize =
|
||||
configDeviceCDCACM[p.device.port][id-1].commInterruptInPacketSize
|
||||
|
||||
comm.deviceInterface[0].endpoint[0].interval =
|
||||
configDeviceCDCACM[p.device.port][id-1].commInterruptInInterval
|
||||
|
||||
// CDC-ACM Data interface
|
||||
data.interfaceNumber =
|
||||
configDeviceCDCACM[p.device.port][id-1].dataInterfaceIndex
|
||||
|
||||
data.deviceInterface[0].endpoint[0].address =
|
||||
configDeviceCDCACM[p.device.port][id-1].dataBulkInEndpoint |
|
||||
specDescriptorEndpointAddressDirectionIn
|
||||
|
||||
data.deviceInterface[0].endpoint[0].maxPacketSize =
|
||||
configDeviceCDCACM[p.device.port][id-1].dataBulkInPacketSize
|
||||
|
||||
data.deviceInterface[0].endpoint[1].address =
|
||||
configDeviceCDCACM[p.device.port][id-1].dataBulkOutEndpoint |
|
||||
specDescriptorEndpointAddressDirectionOut
|
||||
|
||||
data.deviceInterface[0].endpoint[1].maxPacketSize =
|
||||
configDeviceCDCACM[p.device.port][id-1].dataBulkOutPacketSize
|
||||
|
||||
// assign our configured CDC-ACM class to the receiver's device and call its
|
||||
// class initialization routine(s).
|
||||
cls := p.device.initClass(id, deviceCDCACMConfigInstance[p.device.port], handler)
|
||||
acm := cls.config[0].driver.(*deviceCDCACM)
|
||||
|
||||
return acm, cls
|
||||
}
|
||||
|
||||
// OK returns true if and only if the receiver s is equal to statusSuccess.
|
||||
//go:inline
|
||||
func (s status) OK() bool { return statusSuccess == s }
|
||||
|
||||
// Error returns a simple descriptive error string of the receiver s.
|
||||
func (s status) Error() string {
|
||||
switch s {
|
||||
case statusSuccess:
|
||||
return ""
|
||||
case statusError:
|
||||
return "failed"
|
||||
case statusBusy:
|
||||
return "busy"
|
||||
case statusInvalidHandle:
|
||||
return "invalid handle"
|
||||
case statusInvalidParameter:
|
||||
return "invalid parameter"
|
||||
case statusInvalidRequest:
|
||||
return "invalid request"
|
||||
case statusControllerNotFound:
|
||||
return "controller not found"
|
||||
case statusInvalidController:
|
||||
return "invalid controller interface"
|
||||
case statusNotSupported:
|
||||
return "configuration not supported"
|
||||
case statusRetry:
|
||||
return "retry enumeration"
|
||||
case statusTransferStall:
|
||||
return "transfer stalled"
|
||||
case statusTransferFailed:
|
||||
return "transfer failed"
|
||||
case statusAllocFail:
|
||||
return "allocation failed"
|
||||
case statusLackSwapBuffer:
|
||||
return "insufficient swap buffer"
|
||||
case statusTransferCancel:
|
||||
return "transfer cancelled"
|
||||
case statusBandwidthFail:
|
||||
return "bandwidth allocation failed"
|
||||
case statusMSDStatusFail:
|
||||
return "mass-storage device failed"
|
||||
case statusEHCIAttached:
|
||||
return "host attached"
|
||||
case statusEHCIDetached:
|
||||
return "host detached"
|
||||
case statusDataOverRun:
|
||||
return "data overrun"
|
||||
case statusNotImplemented:
|
||||
return "feature not implemented"
|
||||
default:
|
||||
return "unknown error"
|
||||
}
|
||||
}
|
||||
|
||||
// leU64 returns a slice containing 8 bytes from the given uint64 u.
|
||||
//
|
||||
// The returned bytes have little-endian ordering; that is, the first element
|
||||
// at index 0 is the least-significant byte in u and index 7 is the most-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func leU64(u uint64) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24),
|
||||
uint8(u >> 32), uint8(u >> 40), uint8(u >> 48), uint8(u >> 56),
|
||||
}
|
||||
}
|
||||
|
||||
// leU32 returns a slice containing 4 bytes from the given uint32 u.
|
||||
//
|
||||
// The returned bytes have little-endian ordering; that is, the first element
|
||||
// at index 0 is the least-significant byte in u and index 3 is the most-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func leU32(u uint32) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u), uint8(u >> 8), uint8(u >> 16), uint8(u >> 24),
|
||||
}
|
||||
}
|
||||
|
||||
// leU16 returns a slice containing 2 bytes from the given uint16 u.
|
||||
//
|
||||
// The returned bytes have little-endian ordering; that is, the first element
|
||||
// at index 0 is the least-significant byte in u and index 1 is the most-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func leU16(u uint16) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u), uint8(u >> 8),
|
||||
}
|
||||
}
|
||||
|
||||
// beU64 returns a slice containing 8 bytes from the given uint64 u.
|
||||
//
|
||||
// The returned bytes have big-endian ordering; that is, the first element at
|
||||
// index 0 is the most-significant byte in u and index 7 is the least-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func beU64(u uint64) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0, 0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u >> 56), uint8(u >> 48), uint8(u >> 40), uint8(u >> 32),
|
||||
uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u),
|
||||
}
|
||||
}
|
||||
|
||||
// beU32 returns a slice containing 4 bytes from the given uint32 u.
|
||||
//
|
||||
// The returned bytes have big-endian ordering; that is, the first element at
|
||||
// index 0 is the most-significant byte in u and index 3 is the least-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func beU32(u uint32) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0, 0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u >> 24), uint8(u >> 16), uint8(u >> 8), uint8(u),
|
||||
}
|
||||
}
|
||||
|
||||
// beU16 returns a slice containing 2 bytes from the given uint16 u.
|
||||
//
|
||||
// The returned bytes have big-endian ordering; that is, the first element at
|
||||
// index 0 is the most-significant byte in u and index 1 is the least-
|
||||
// significant byte.
|
||||
//go:inline
|
||||
func beU16(u uint16) []uint8 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return []uint8{0, 0}
|
||||
}
|
||||
return []uint8{
|
||||
uint8(u >> 8), uint8(u),
|
||||
}
|
||||
}
|
||||
|
||||
// revU64 returns the given uint64 u with bytes in the reverse order.
|
||||
//go:inline
|
||||
func revU64(u uint64) uint64 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return 0
|
||||
}
|
||||
return ((u & 0x00000000000000FF) << 56) |
|
||||
((u & 0x000000000000FF00) << 40) |
|
||||
((u & 0x0000000000FF0000) << 24) |
|
||||
((u & 0x00000000FF000000) << 8) |
|
||||
((u & 0x000000FF00000000) >> 8) |
|
||||
((u & 0x0000FF0000000000) >> 24) |
|
||||
((u & 0x00FF000000000000) >> 40) |
|
||||
((u & 0xFF00000000000000) >> 56)
|
||||
}
|
||||
|
||||
// revU32 returns the given uint32 u with bytes in the reverse order.
|
||||
//go:inline
|
||||
func revU32(u uint32) uint32 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return 0
|
||||
}
|
||||
return ((u & 0x000000FF) << 24) | ((u & 0x0000FF00) << 8) |
|
||||
((u & 0x00FF0000) >> 8) | ((u & 0xFF000000) >> 24)
|
||||
}
|
||||
|
||||
// revU16 returns the given uint16 u with bytes in the reverse order.
|
||||
//go:inline
|
||||
func revU16(u uint16) uint16 {
|
||||
if u == 0 {
|
||||
// skip all processing for the common case (u = 0)
|
||||
return 0
|
||||
}
|
||||
return ((u & 0x00FF) << 8) | ((u & 0xFF00) >> 8)
|
||||
}
|
||||
|
||||
// packU64 returns a uint64 constructed by concatenating the bytes in slice b.
|
||||
//
|
||||
// The least-significant byte in the returned value is the first element at
|
||||
// index 0 in b and the most significant byte is index 7, if given. If fewer
|
||||
// than 8 elements are given in b, the corresponding bytes in the returned value
|
||||
// are all 0.
|
||||
//go:inline
|
||||
func packU64(b []uint8) (u uint64) {
|
||||
for i := 0; i < 8 && i < len(b); i++ {
|
||||
u |= uint64(b[i]) << (i * 8)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// packU32 returns a uint32 constructed by concatenating the bytes in slice b.
|
||||
//
|
||||
// The least-significant byte in the returned value is the first element at
|
||||
// index 0 in b and the most significant byte is index 3, if given. If fewer
|
||||
// than 4 elements are given in b, the corresponding bytes in the returned value
|
||||
// are all 0.
|
||||
//go:inline
|
||||
func packU32(b []uint8) (u uint32) {
|
||||
for i := 0; i < 4 && i < len(b); i++ {
|
||||
u |= uint32(b[i]) << (i * 8)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// packU16 returns a uint16 constructed by concatenating the bytes in slice b.
|
||||
//
|
||||
// The least-significant byte in the returned value is the first element at
|
||||
// index 0 in b and the most significant byte is index 1, if given. If fewer
|
||||
// than 2 elements are given in b, the corresponding bytes in the returned value
|
||||
// are all 0.
|
||||
//go:inline
|
||||
func packU16(b []uint8) (u uint16) {
|
||||
for i := 0; i < 2 && i < len(b); i++ {
|
||||
u |= uint16(b[i]) << (i * 8)
|
||||
}
|
||||
return
|
||||
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package usb2
|
||||
package usb
|
||||
|
||||
// leU64 returns a slice containing 8 bytes from the given uint64 u.
|
||||
//
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build arm
|
||||
|
||||
package usb2
|
||||
package usb
|
||||
|
||||
import "device/arm"
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
package usb2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUARTInvalidPort = errors.New("invalid USB port")
|
||||
ErrUARTInvalidCore = errors.New("invalid USB core")
|
||||
ErrUARTEmptyBuffer = errors.New("USB receive buffer empty")
|
||||
ErrUARTWriteFailed = errors.New("USB write failure")
|
||||
)
|
||||
|
||||
type (
|
||||
UARTConfig struct {
|
||||
BaudRate uint32
|
||||
}
|
||||
|
||||
// UART represents a virtual serial (UART) device emulation using the USB
|
||||
// CDC-ACM device class driver.
|
||||
UART struct {
|
||||
port int // USB port (core index, e.g., 0-1)
|
||||
core *core
|
||||
}
|
||||
)
|
||||
|
||||
func (uart *UART) Configure(config UARTConfig) error {
|
||||
|
||||
if uart.port >= CoreCount || uart.port >= dcdCount {
|
||||
return ErrUARTInvalidPort
|
||||
}
|
||||
|
||||
// verify we have a free USB port and take ownership of it
|
||||
var st status
|
||||
uart.core, st = initCore(uart.port, class{id: classDeviceCDCACM, config: 1})
|
||||
if !st.ok() {
|
||||
return ErrUARTInvalidPort
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Buffered returns the number of bytes currently stored in the RX buffer.
|
||||
func (uart UART) Buffered() int {
|
||||
dc, ok := uart.core.dc.(*deviceController)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return dc.uartAvailable()
|
||||
}
|
||||
|
||||
// ReadByte reads a single byte from the RX buffer.
|
||||
// If there is no data in the buffer, returns an error.
|
||||
func (uart UART) ReadByte() (byte, error) {
|
||||
dc, ok := uart.core.dc.(*deviceController)
|
||||
if !ok {
|
||||
return 0, ErrUARTInvalidCore
|
||||
}
|
||||
n, ok := dc.uartReadByte()
|
||||
if !ok {
|
||||
return 0, ErrUARTEmptyBuffer
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Read from the RX buffer.
|
||||
func (uart UART) Read(data []byte) (n int, err error) {
|
||||
dc, ok := uart.core.dc.(*deviceController)
|
||||
if !ok {
|
||||
return 0, ErrUARTInvalidCore
|
||||
}
|
||||
return dc.uartRead(data), nil
|
||||
}
|
||||
|
||||
// WriteByte writes a single byte of data to the UART interface.
|
||||
func (uart UART) WriteByte(c byte) error {
|
||||
dc, ok := uart.core.dc.(*deviceController)
|
||||
if !ok {
|
||||
return ErrUARTInvalidCore
|
||||
}
|
||||
if !dc.uartWriteByte(c) {
|
||||
return ErrUARTWriteFailed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Write data to the UART.
|
||||
func (uart UART) Write(data []byte) (n int, err error) {
|
||||
dc, ok := uart.core.dc.(*deviceController)
|
||||
if !ok {
|
||||
return 0, ErrUARTInvalidCore
|
||||
}
|
||||
return dc.uartWrite(data), nil
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
package usb2
|
||||
|
||||
// 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
|
||||
|
||||
// coreInstance provides statically-allocated instances of each USB core
|
||||
// configured on this platform.
|
||||
var coreInstance [CoreCount]core
|
||||
|
||||
// status represents the return code of a subroutine.
|
||||
type status uint8
|
||||
|
||||
// Constant definitions for all status codes used within the package.
|
||||
const (
|
||||
statusOK status = iota // Success
|
||||
statusBusy // Busy
|
||||
statusRetry // Retry
|
||||
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
|
||||
// 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) {
|
||||
|
||||
if port < 0 || port >= CoreCount || 0 == class.config {
|
||||
return nil, statusInvalid
|
||||
}
|
||||
|
||||
if modeIdle != coreInstance[port].mode {
|
||||
// Check if requested port is already configured as requested class. If so,
|
||||
// just return a reference to the existing core instead of an error.
|
||||
// For instance, this will allow TinyGo examples that try to reconfigure the
|
||||
// USB (CDC-ACM) UART port (which is already configured by the runtime) to
|
||||
// continue without error.
|
||||
if coreInstance[port].mode == class.mode() {
|
||||
switch class.mode() {
|
||||
case modeDevice:
|
||||
if coreInstance[port].dc.class().equals(class) {
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
case modeHost:
|
||||
if coreInstance[port].hc.class().equals(class) {
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, statusBusy
|
||||
}
|
||||
|
||||
switch class.mode() {
|
||||
case modeDevice:
|
||||
// Allocate a free device controller and install interrupts
|
||||
dc, st := initDCD(port, class)
|
||||
if !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
// Initialize buffers and device descriptors
|
||||
if st = dc.init(); !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
coreInstance[port].port = port
|
||||
coreInstance[port].mode = 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
|
||||
}
|
||||
|
||||
case modeHost:
|
||||
// Allocate a free host controller and install interrupts
|
||||
hc, st := initHCD(port, class)
|
||||
if !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
// Initialize buffers and device descriptors
|
||||
if st = hc.init(); !st.ok() {
|
||||
return nil, st
|
||||
}
|
||||
coreInstance[port].port = port
|
||||
coreInstance[port].mode = 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
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, statusInvalid
|
||||
}
|
||||
|
||||
return &coreInstance[port], statusOK
|
||||
}
|
||||
Reference in New Issue
Block a user