Files
tinygo/src/machine/usb/usb.go
T
2021-05-28 18:11:58 -05:00

417 lines
13 KiB
Go

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
}