mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-13 23:43:40 +00:00
begin USB refactor with package machine/usb2
This commit is contained in:
@@ -0,0 +1,416 @@
|
||||
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
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
func init() {
|
||||
// ensure all ports are in idle state by default
|
||||
for i := range portInstance {
|
||||
portInstance[i].mode = modeIdle
|
||||
}
|
||||
}
|
||||
|
||||
func ProcessMessages() (err error) {
|
||||
for i := range portInstance {
|
||||
s := portInstance[i].process()
|
||||
if !s.OK() && nil == err {
|
||||
err = s
|
||||
}
|
||||
}
|
||||
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 {
|
||||
case modeDevice:
|
||||
if s := portInstance[port].device.init(port); !s.OK() {
|
||||
return nil, s
|
||||
}
|
||||
case modeHost:
|
||||
if s := portInstance[port].host.init(port); !s.OK() {
|
||||
return nil, s
|
||||
}
|
||||
}
|
||||
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()
|
||||
default:
|
||||
return statusInvalidController
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user