mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-06 12:03:41 +00:00
417 lines
13 KiB
Go
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
|
|
}
|