rename package machine/usb2 to machine/usb

This commit is contained in:
ardnew
2021-04-20 21:16:06 -05:00
committed by sago35
parent 69e8257e7b
commit 079eace344
24 changed files with 207 additions and 4502 deletions
+3 -3
View File
@@ -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.
-25
View File
@@ -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
}
-233
View File
@@ -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
-162
View File
@@ -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
-135
View File
@@ -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
)
-690
View File
@@ -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
}
-288
View File
@@ -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.
-25
View File
@@ -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
}
-13
View File
@@ -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{}
-167
View File
@@ -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
View File
@@ -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
View File
@@ -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"
-93
View File
@@ -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
}
-157
View File
@@ -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
}