diff --git a/src/examples/usb/serial/echo.go b/src/examples/usb/serial/echo.go index b4a61fea7..d430d8b1b 100644 --- a/src/examples/usb/serial/echo.go +++ b/src/examples/usb/serial/echo.go @@ -8,7 +8,7 @@ import ( ) func main() { - uart := machine.UART0 + uart := machine.Serial uart.Write([]byte("Echo console enabled. Type something then press enter:\r\n")) input := make([]byte, 4096) diff --git a/src/machine/board_grandcentral-m4.go b/src/machine/board_grandcentral-m4.go index 61201826f..64b649a69 100644 --- a/src/machine/board_grandcentral-m4.go +++ b/src/machine/board_grandcentral-m4.go @@ -3,6 +3,10 @@ package machine +import ( + "machine/usb" +) + // Digital pins const ( // = Pin Alt. Function SERCOM PWM Timer Interrupt @@ -248,6 +252,8 @@ const ( RESET_MAGIC_VALUE = 0xF01669EF // Used to reset into bootloader ) +var USB = usb.UART{Port: 0} + // USB CDC pins const ( USBCDC_HOSTEN_PIN = D77 // (PA27) host enable diff --git a/src/machine/machine_atsamd51.go b/src/machine/machine_atsamd51.go index 65effd594..a488af15a 100644 --- a/src/machine/machine_atsamd51.go +++ b/src/machine/machine_atsamd51.go @@ -1985,10 +1985,10 @@ type USBCDC struct { configured bool } -var ( - // USB is a USB CDC interface. - USB = &USBCDC{Buffer: NewRingBuffer()} -) +// var ( +// // USB is a USB CDC interface. +// USB = &USBCDC{Buffer: NewRingBuffer()} +// ) const ( usbcdcTxSizeMask uint8 = 0x3F @@ -2163,10 +2163,10 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) { sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE) // enable IRQ at highest priority - interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable() - interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable() - interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable() - interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable() + //interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable() + //interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable() + //interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable() + //interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable() usbcdc.configured = true } @@ -2240,7 +2240,7 @@ func handleUSBIRQ(interrupt.Interrupt) { // Start of frame if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 { - USB.Flush() + //USB.Flush() // if you want to blink LED showing traffic, this would be the place... } @@ -2300,7 +2300,7 @@ func handleUSBIRQ(interrupt.Interrupt) { setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) if i == usb_CDC_ENDPOINT_IN { - USB.waitTxc = false + //USB.waitTxc = false } } } @@ -2614,7 +2614,7 @@ func handleEndpoint(ep uint32) { // move to ring buffer for i := 0; i < count; i++ { - USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF))) + //USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF))) } // set byte count to zero diff --git a/src/machine/serial-usb.go b/src/machine/serial-usb.go index ac484f87a..a3dd4abe5 100644 --- a/src/machine/serial-usb.go +++ b/src/machine/serial-usb.go @@ -5,3 +5,11 @@ package machine // Serial is implemented via USB (USB-CDC). var Serial = USB + +func init() { + + // configure pins + USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom}) + USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom}) + +} diff --git a/src/machine/usb/dcd.go b/src/machine/usb/dcd.go index db0189f62..0b5aa5994 100644 --- a/src/machine/usb/dcd.go +++ b/src/machine/usb/dcd.go @@ -7,7 +7,10 @@ package usb // implemented for each target, providing common/shared functionality and // defining a standard interface with which the dhw must adhere. -import "unsafe" +import ( + "runtime/volatile" + "unsafe" +) // dcdCount defines the number of USB cores to configure for device mode. It is // computed as the sum of all declared device configuration descriptors. @@ -29,6 +32,8 @@ type dcd struct { port int // USB port index cc class // USB device class id int // USB device controller index + + st volatile.Register8 // USB device state } // initDCD initializes and assigns a free device controller instance to the @@ -54,6 +59,7 @@ func initDCD(port int, speed Speed, class class) (*dcd, status) { dcdInstance[i].port = port dcdInstance[i].cc = class dcdInstance[i].id = i + dcdInstance[i].setState(dcdStateNotReady) return &dcdInstance[i], statusOK } } @@ -77,24 +83,19 @@ type dcdSetup struct { // setupFrom decodes and returns a USB standard setup packet located at the // memory address pointed to by addr. -func setupFrom(addr uintptr) dcdSetup { +func setupFrom(addr uintptr) (s dcdSetup) { var u uint64 - for i := uintptr(0); i < 8; i++ { + for i := uintptr(0); i < dcdSetupSize; i++ { u |= uint64(*(*uint8)(unsafe.Pointer(addr + i))) << (i << 3) } - return dcdSetup{ - bmRequestType: uint8(u & 0xFF), - bRequest: uint8((u & 0xFF00) >> 8), - wValue: uint16((u & 0xFFFF0000) >> 16), - wIndex: uint16((u & 0xFFFF00000000) >> 32), - wLength: uint16((u & 0xFFFF000000000000) >> 48), - } + s.set(u) + return } // setup decodes and returns a USB standard setup packet stored in the given // byte slice b. func setup(b []uint8) dcdSetup { - if len(b) >= 8 { + if len(b) >= int(dcdSetupSize) { return dcdSetup{ bmRequestType: b[0], bRequest: b[1], @@ -106,7 +107,17 @@ func setup(b []uint8) dcdSetup { return dcdSetup{} } +//go:inline +func (s *dcdSetup) set(u uint64) { + s.bmRequestType = uint8(u & 0xFF) + s.bRequest = uint8((u & 0xFF00) >> 8) + s.wValue = uint16((u & 0xFFFF0000) >> 16) + s.wIndex = uint16((u & 0xFFFF00000000) >> 32) + s.wLength = uint16((u & 0xFFFF000000000000) >> 48) +} + // pack returns the receiver USB standard setup packet s encoded as uint64. +//go:inline func (s dcdSetup) pack() uint64 { return ((uint64(s.bmRequestType) & 0xFF) << 0) | ((uint64(s.bRequest) & 0xFF) << 8) | @@ -115,6 +126,54 @@ func (s dcdSetup) pack() uint64 { ((uint64(s.wLength) & 0xFFFF) << 48) } +// direction parses the direction bit from the bmRequestType field of a SETUP +// packet, returning 0 for OUT (Rx) and 1 for IN (Tx) requests. +//go:inline +func (s dcdSetup) direction() uint8 { + return (s.bmRequestType & descRequestTypeDirMsk) >> descRequestTypeDirPos +} + +//go:inline +func (s dcdSetup) equals(t dcdSetup) bool { + return s.bmRequestType == t.bmRequestType && s.bRequest == t.bRequest && + s.wValue == t.wValue && s.wIndex == t.wIndex && s.wLength == t.wLength +} + +// dcdState defines the current state of the device class driver. +type dcdState uint8 + +const ( + dcdStateNotReady dcdState = iota // initial state, before END_OF_RESET + dcdStateDefault // after END_OF_RESET, before SET_ADDRESS + dcdStateAddressed // after SET_ADDRESS, before SET_CONFIGURATION + dcdStateConfigured // after SET_CONFIGURATION, operational state + dcdStateSuspended // while operational, after SUSPEND +) + +func (d *dcd) state() dcdState { return dcdState(d.st.Get()) } + +func (d *dcd) setState(state dcdState) (ok bool) { + curr := d.state() + switch state { + case dcdStateNotReady: + ok = true + case dcdStateDefault: + ok = curr == dcdStateNotReady || curr == dcdStateDefault + case dcdStateAddressed: + ok = curr == dcdStateDefault + case dcdStateConfigured: + ok = curr == dcdStateAddressed || curr == dcdStateConfigured || curr == dcdStateSuspended + case dcdStateSuspended: + ok = curr == dcdStateAddressed || curr == dcdStateConfigured || curr == dcdStateSuspended + default: + ok = false + } + if ok { + d.st.Set(uint8(state)) + } + return +} + // dcdEvent is used to describe virtual interrupts on the USB bus to a device // controller. // @@ -132,47 +191,85 @@ type dcdEvent struct { // Enumerated constants for all possible USB device controller interrupt codes. const ( - dcdEventInvalid uint8 = iota // Invalid interrupt - dcdEventStatusReset // USB reset received - dcdEventStatusRun // USB controller entered run state - dcdEventStatusSuspend // USB suspend received - dcdEventStatusError // USB error condition detected on bus - dcdEventControlSetup // USB setup received - dcdEventPeripheralReady // USB PHY powered and ready to _go_ - dcdEventTransactComplete // USB transaction complete - dcdEventTimer // USB (system) timer + dcdEventInvalid uint8 = iota // Invalid interrupt + dcdEventStatusReset // USB RESET received + dcdEventStatusResume // USB RESUME condition + dcdEventStatusSuspend // USB SUSPEND received + dcdEventStatusError // USB error condition detected on bus + dcdEventDeviceReady // USB PHY powered and ready to _go_ + dcdEventDeviceAddress // USB device SET_ADDRESS complete + dcdEventDeviceConfiguration // USB device SET_CONFIGURATION complete + dcdEventControlSetup // USB SETUP received + dcdEventControlComplete // USB control request complete + dcdEventTransferComplete // USB data transfer complete + dcdEventTimer // USB (system) timer ) func (d *dcd) event(ev dcdEvent) { switch ev.id { - case dcdEventInvalid: + case dcdEventStatusReset: - d.endpointMask = 0 + d.setState(dcdStateNotReady) - case dcdEventPeripheralReady: - // Configure and enable control endpoint 0 - d.endpointEnable(0, true, 0) + case dcdEventStatusResume: + d.setState(dcdStateConfigured) - case dcdEventStatusRun: case dcdEventStatusSuspend: - case dcdEventStatusError: - case dcdEventControlSetup: - // On control endpoint 0 setup events, the ev.setup field will be defined - d.stage = d.controlSetup(ev.setup) - switch d.stage { - case dcdStageSetup: - case dcdStageDataIn: - case dcdStageDataOut: - case dcdStageStatusIn: - case dcdStageStatusOut: - case dcdStageStall: - d.controlStall() + d.setState(dcdStateSuspended) + + case dcdEventDeviceReady: + if d.setState(dcdStateDefault) { + // Configure and enable control endpoint 0 + d.endpointEnable(0, true, 0) } - case dcdEventTransactComplete: - case dcdEventTimer: + case dcdEventDeviceAddress: + // -- ** IMPORTANT ** -- + // dcdEventDeviceAddress must be triggered by the target driver, because + // different MCUs require setting the device address at different times + // during the enumeration process. + d.setState(dcdStateAddressed) + + case dcdEventDeviceConfiguration: + d.setState(dcdStateConfigured) + + case dcdEventControlSetup: + // On control endpoint 0 setup events, the ev.setup field will be defined. + // We overwrite the receiver's setup field, leaving it unmodified throughout + // all transactions of a control transfer. It is only cleared once the + // completion event dcdEventControlComplete has been called and finished + // processing, or if its initial processing fails due to error. + d.setup = ev.setup + d.stage = d.controlSetup(ev.setup) + switch d.stage { + case dcdStageDataIn, dcdStageDataOut: + // TBD: control endpoint data transfer + + case dcdStageStatusIn, dcdStageStatusOut: + // TBD: control endpoint status transfer + + case dcdStageStall: + d.controlStall(true, ev.setup.direction()) + + case dcdStageSetup: + fallthrough + default: + // TBD: no stage transition occurred + } + + case dcdEventControlComplete: + d.controlComplete() + // clear the active SETUP packet once the control transfer completes. + d.setup = dcdSetup{} + + case dcdEventTransferComplete: + // TBD: data endpoint transfer complete + + case dcdEventInvalid, dcdEventStatusError, dcdEventTimer: + fallthrough default: + // TBD: unhandled events } } @@ -192,9 +289,6 @@ const ( // controlSetup handles setup messages on control endpoint 0. func (d *dcd) controlSetup(sup dcdSetup) dcdStage { - // Reset endpoint 0 notify mask - d.controlMask = 0 - // First, switch on the type of request (standard, class, or vendor) switch sup.bmRequestType & descRequestTypeTypeMsk { @@ -215,7 +309,7 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { case descRequestStandardSetAddress: d.setDeviceAddress(sup.wValue) d.controlReceive(uintptr(0), 0, false) - return dcdStageSetup + return dcdStageStatusOut // SET CONFIGURATION (0x09): case descRequestStandardSetConfiguration: @@ -224,6 +318,7 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // Use default if invalid index received d.cc.config = 1 } + d.event(dcdEvent{id: dcdEventDeviceConfiguration}) // Respond based on our device class configuration switch d.cc.id { @@ -244,7 +339,7 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { default: // Unhandled device class } - return dcdStageSetup + return dcdStageStatusOut default: // Unhandled request @@ -258,11 +353,10 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // GET STATUS (0x00): case descRequestStandardGetStatus: - d.controlReply[0] = 0 - d.controlReply[1] = 0 d.controlTransmit( - uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false) - return dcdStageSetup + d.controlStatusBuffer([]uint8{0, 0}), + 2, false) + return dcdStageDataIn // GET DESCRIPTOR (0x06): case descRequestStandardGetDescriptor: @@ -273,12 +367,12 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // CDC-ACM (single) case classDeviceCDCACM: d.controlDescriptorCDCACM(sup) - return dcdStageSetup + return dcdStageDataIn // HID case classDeviceHID: d.controlDescriptorHID(sup) - return dcdStageSetup + return dcdStageDataIn default: // Unhandled device class @@ -286,10 +380,12 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // GET CONFIGURATION (0x08): case descRequestStandardGetConfiguration: - d.controlReply[0] = uint8(d.cc.config) d.controlTransmit( - uintptr(unsafe.Pointer(&d.controlReply[0])), 1, false) - return dcdStageSetup + d.controlStatusBuffer([]uint8{ + uint8(d.cc.config), + }), + 1, false) + return dcdStageDataIn default: // Unhandled request @@ -310,12 +406,12 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // CDC-ACM (single) case classDeviceCDCACM: d.controlDescriptorCDCACM(sup) - return dcdStageSetup + return dcdStageDataIn // HID case classDeviceHID: d.controlDescriptorHID(sup) - return dcdStageSetup + return dcdStageDataIn default: // Unhandled device class @@ -330,7 +426,7 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // HID case classDeviceHID: d.controlDescriptorHID(sup) - return dcdStageSetup + return dcdStageDataIn default: // Unhandled device class @@ -350,13 +446,13 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { case descRequestStandardClearFeature: d.endpointClearFeature(uint8(sup.wIndex)) d.controlReceive(uintptr(0), 0, false) - return dcdStageSetup + return dcdStageStatusOut // SET FEATURE (0x03): case descRequestStandardSetFeature: d.endpointSetFeature(uint8(sup.wIndex)) d.controlReceive(uintptr(0), 0, false) - return dcdStageSetup + return dcdStageStatusOut default: // Unhandled request @@ -371,11 +467,13 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // GET STATUS (0x00): case descRequestStandardGetStatus: status := d.endpointStatus(uint8(sup.wIndex)) - d.controlReply[0] = uint8(status) - d.controlReply[1] = uint8(status >> 8) d.controlTransmit( - uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false) - return dcdStageSetup + d.controlStatusBuffer([]uint8{ + uint8(status), + uint8(status >> 8), + }), + 2, false) + return dcdStageDataIn default: // Unhandled request @@ -407,14 +505,14 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // CDC-ACM (single) case classDeviceCDCACM: // line coding must contain exactly 7 bytes - if descCDCACMCodingSize == sup.wLength { + if uint16(descCDCACMCodingSize) == sup.wLength { d.setup = sup d.controlReceive( uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].cx[0])), - descCDCACMCodingSize, true) + uint32(descCDCACMCodingSize), true) // CDC Line Coding packet receipt handling occurs in method // controlComplete(). - return dcdStageSetup + return dcdStageDataOut } default: @@ -438,7 +536,7 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // DTR is bit 0 (mask 0x01), RTS is bit 1 (mask 0x02) d.uartSetLineState(0 != sup.wValue&0x01, 0 != sup.wValue&0x02) d.controlReceive(uintptr(0), 0, false) - return dcdStageSetup + return dcdStageStatusOut default: // Unhandled device interface @@ -457,7 +555,7 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // CDC-ACM (single) case classDeviceCDCACM: d.controlReceive(uintptr(0), 0, false) - return dcdStageSetup + return dcdStageStatusOut default: // Unhandled device class @@ -471,13 +569,13 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // HID case classDeviceHID: - if sup.wLength <= descHIDCxSize { + if sup.wLength <= descHIDSxSize { d.setup = sup descHID[d.cc.config-1].cx[0] = 0xE9 d.controlReceive( uintptr(unsafe.Pointer(&descHID[d.cc.config-1].cx[0])), uint32(sup.wLength), true) - return dcdStageSetup + return dcdStageDataOut } default: @@ -493,9 +591,11 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { // HID case classDeviceHID: idleRate := sup.wValue >> 8 + // TBD: do we need to handle this request? wIndex contains the target + // interface of the request. _ = idleRate d.controlReceive(uintptr(0), 0, false) - return dcdStageSetup + return dcdStageStatusOut default: // Unhandled device class @@ -521,12 +621,13 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { case classDeviceHID: reportType := uint8(sup.wValue >> 8) reportID := uint8(sup.wValue) + // TBD: do we need to handle this request? wIndex contains the target + // interface of the request. _, _ = reportType, reportID - d.controlReply[0] = 0 - d.controlReply[1] = 0 d.controlTransmit( - uintptr(unsafe.Pointer(&d.controlReply[0])), 2, false) - return dcdStageSetup + d.controlStatusBuffer([]uint8{0, 0}), + 2, false) + return dcdStageDataIn default: // Unhandled device class @@ -551,10 +652,7 @@ func (d *dcd) controlSetup(sup dcdSetup) dcdStage { } // controlComplete handles the setup completion of control endpoint 0. -func (d *dcd) controlComplete(status uint32) { - - // Reset endpoint 0 notify mask - d.controlMask = 0 +func (d *dcd) controlComplete() { // First, switch on the type of request (standard, class, or vendor) switch d.setup.bmRequestType & descRequestTypeTypeMsk { diff --git a/src/machine/usb/desc.go b/src/machine/usb/desc.go index 3cecccd17..0ca06222d 100644 --- a/src/machine/usb/desc.go +++ b/src/machine/usb/desc.go @@ -1,8 +1,10 @@ package usb +import "unsafe" + const descUSBSpecVersion = uint16(0x0200) // USB 2.0 -const descLanguageEnglish = uint16(0x0409) +const descLanguageEnglish = uint16(0x0409) // (US) English // USB constants defined per specification. const ( @@ -135,6 +137,11 @@ const ( descDeviceCapExtAttrBESLPos = 2 ) +const ( + descDirOut = descRequestTypeDirOut >> descRequestTypeDirPos + descDirIn = descRequestTypeDirIn >> descRequestTypeDirPos +) + // device returns the enumerated device descriptor value, defined per USB // specification, for the receiver Speed s. func (s Speed) device() uint32 { @@ -153,7 +160,7 @@ func (s Speed) device() uint32 { } const ( - // Attributes of all endpoint descriptor configurations. + // Common attributes for all endpoint descriptor configurations. descEndptConfigAttr = descConfigAttrD7Msk | // Bit 7: reserved (1) (0 << descConfigAttrSelfPoweredPos) | // Bit 6: self-powered (0 << descConfigAttrRemoteWakeupPos) | // Bit 5: remote wakeup @@ -161,8 +168,8 @@ const ( descEndptConfigAttrRxPos = 0 descEndptConfigAttrTxPos = 16 - descEndptConfigAttrRxMsk = (descEndptConfigAttr | descEndptAttrSyncTypeMsk) << descEndptConfigAttrRxPos - descEndptConfigAttrTxMsk = (descEndptConfigAttr | descEndptAttrSyncTypeMsk) << descEndptConfigAttrTxPos + descEndptConfigAttrRxMsk = (descEndptAttrSyncTypeMsk | descEndptConfigAttr) << descEndptConfigAttrRxPos + descEndptConfigAttrTxMsk = (descEndptAttrSyncTypeMsk | descEndptConfigAttr) << descEndptConfigAttrTxPos descEndptConfigAttrRxUnused = 0x02 << descEndptConfigAttrRxPos descEndptConfigAttrTxUnused = 0x02 << descEndptConfigAttrTxPos @@ -419,8 +426,11 @@ const ( // for each string) seems a good compromise. ) +// descEndpointInvalid represents an invalid endpoint address. +const descEndpointInvalid = ^uint8(descEndptAddrNumberMsk | descEndptAddrDirectionMsk) + // descCDCACMCodingSize defines the length of a CDC-ACM UART line coding buffer. -const descCDCACMCodingSize = 7 +const descCDCACMCodingSize = unsafe.Sizeof(descCDCACMLineCoding{}) // descCDCACMLineCoding represents an emulated UART's line configuration. type descCDCACMLineCoding struct { @@ -455,6 +465,9 @@ const ( type descCDCACMClass struct { *descCDCACMClassData // Target-defined, class-specific data + line descCDCACMLineCoding + term struct{ dtr, rts bool } + locale *[descCDCACMLanguageCount]descStringLanguage // string descriptors device *[descLengthDevice]uint8 // device descriptor qualif *[descLengthQualification]uint8 // device qualification descriptor diff --git a/src/machine/usb/desc_atsamd51.go b/src/machine/usb/desc_atsamd51.go index 2899c19bf..24f9ca1f2 100644 --- a/src/machine/usb/desc_atsamd51.go +++ b/src/machine/usb/desc_atsamd51.go @@ -41,7 +41,7 @@ const ( descBankOut = 0 // descriptor bank 0 holds OUT endpoints descBankIn = 1 // descriptor bank 1 holds IN endpoints - descControlPacketSize = 16 + descControlPacketSize = 64 ) // Constants for USB CDC-ACM device classes. @@ -64,7 +64,8 @@ const ( // | than the data payload specified by PCKSIZE.SIZE minus two, both CRC // | data bytes are written to the data buffer. // Thus, we need to allocate 2 extra bytes for control endpoint 0 Rx (OUT). - descCDCACMCxSize = 8 + 2 + descCDCACMSxSize = 8 + 2 + descCDCACMCxSize = descControlPacketSize // CDC-ACM Data Buffers @@ -85,7 +86,7 @@ const ( // CDC-ACM Endpoint Configurations for Full-Speed Device descCDCACMStatusFSInterval = 5 // Status - descCDCACMStatusFSPacketSize = 16 // (full-speed) + descCDCACMStatusFSPacketSize = 64 // (full-speed) descCDCACMDataRxFSPacketSize = 64 // Data Rx (full-speed) descCDCACMDataTxFSPacketSize = 64 // Data Tx (full-speed) @@ -115,7 +116,8 @@ const ( // | than the data payload specified by PCKSIZE.SIZE minus two, both CRC // | data bytes are written to the data buffer. // Thus, we need to allocate 2 extra bytes for control endpoint 0 Rx (OUT). - descHIDCxSize = 8 + 2 + descHIDSxSize = 8 + 2 + descHIDCxSize = descControlPacketSize // HID Serial Buffers @@ -192,10 +194,15 @@ const ( // DMA controller the buffer and transfer properties for each endpoint, for the // default CDC-ACM (single) device class configuration (index 1). //go:align 32 -var descCDCACM0ED [descCDCACMEDCount]dhwEndptAddrDesc +var descCDCACM0ED [descCDCACMEDCount]dhwEPAddrDesc -// descCDCACM0Cx is the buffer for control/status data received on endpoint 0 of -// the default CDC-ACM (single) device class configuration (index 1). +// descCDCACM0Sx is the receive (Rx) buffer for setup packets on control endpoint +// 0 of the default CDC-ACM (single) device class configuration (index 1). +//go:align 32 +var descCDCACM0Sx [descCDCACMSxSize]uint8 + +// descCDCACM0Cx is the transmit (Tx) buffer for control/status packets on control +// endpoint 0 of the default CDC-ACM (single) device class configuration (index 1). //go:align 32 var descCDCACM0Cx [descCDCACMCxSize]uint8 @@ -227,9 +234,10 @@ type descCDCACMClassData struct { // CDC-ACM Control Buffers - ed *[descCDCACMEDCount]dhwEndptAddrDesc // endpoint descriptors + ed *[descCDCACMEDCount]dhwEPAddrDesc // endpoint descriptors - cx *[descCDCACMCxSize]uint8 // control endpoint 0 Rx/Tx transfer buffer + sx *[descCDCACMSxSize]uint8 // control endpoint 0 Rx (OUT) setup packets + cx *[descCDCACMCxSize]uint8 // control endpoint 0 Tx (IN) control/status packets dx *[descCDCACMConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer // CDC-ACM Data Buffers @@ -254,6 +262,7 @@ var descCDCACMData = [dcdCount]descCDCACMClassData{ ed: &descCDCACM0ED, + sx: &descCDCACM0Sx, cx: &descCDCACM0Cx, dx: &descCDCACM0Dx, @@ -272,10 +281,15 @@ var descCDCACMData = [dcdCount]descCDCACMClassData{ // DMA controller the buffer and transfer properties for each endpoint, for the // default HID device class configuration (index 1). //go:align 32 -var descHID0ED [descHIDEDCount]dhwEndptAddrDesc +var descHID0ED [descHIDEDCount]dhwEPAddrDesc -// descHID0Cx is the buffer for control/status data received on endpoint 0 of -// the default HID device class configuration (index 1). +// descHID0Sx is the receive (Rx) buffer for setup packets on control endpoint 0 +// of the default HID device class configuration (index 1). +//go:align 32 +var descHID0Sx [descHIDSxSize]uint8 + +// descHID0Cx is the transmit (Tx) buffer for control/status packets on control +// endpoint 0 of the default CDC-ACM (single) device class configuration (index 1). //go:align 32 var descHID0Cx [descHIDCxSize]uint8 @@ -339,9 +353,10 @@ type descHIDClassData struct { // HID Control Buffers - ed *[descHIDEDCount]dhwEndptAddrDesc // endpoint descriptors + ed *[descHIDEDCount]dhwEPAddrDesc // endpoint descriptors - cx *[descHIDCxSize]uint8 // control endpoint 0 Rx/Tx transfer buffer + sx *[descHIDSxSize]uint8 // control endpoint 0 Rx (OUT) setup packets + cx *[descHIDCxSize]uint8 // control endpoint 0 Tx (IN) control/status packets dx *[descHIDConfigSize]uint8 // control endpoint 0 Tx (IN) descriptor transfer buffer // HID Serial Buffers @@ -391,6 +406,7 @@ var descHIDData = [dcdCount]descHIDClassData{ ed: &descHID0ED, + sx: &descHID0Sx, cx: &descHID0Cx, dx: &descHID0Dx, diff --git a/src/machine/usb/desc_mimxrt1062.go b/src/machine/usb/desc_mimxrt1062.go index a5214d963..ff25de7e1 100644 --- a/src/machine/usb/desc_mimxrt1062.go +++ b/src/machine/usb/desc_mimxrt1062.go @@ -1,3 +1,4 @@ +//go:build mimxrt1062 // +build mimxrt1062 package usb diff --git a/src/machine/usb/dhw_atsamd51.go b/src/machine/usb/dhw_atsamd51.go index ae5c8778a..cc46ec5a9 100644 --- a/src/machine/usb/dhw_atsamd51.go +++ b/src/machine/usb/dhw_atsamd51.go @@ -30,14 +30,38 @@ type dhw struct { speed Speed - controlReply [8]uint8 - controlMask uint32 - endpointMask uint32 - setup dcdSetup - stage dcdStage + ready bool // has init() been called + + ep [descMaxEndpoints]dhwEPAddrStatus + + log [2048][64]byte + logCount uint + + setup dcdSetup + stage dcdStage + address uint16 } -func runBootloader() {} +var a uint + +func (d *dhw) logEvent(s string) { + d.enableInterrupts(false) + copy(d.log[d.logCount][:], s) + d.logCount++ + if d.logCount > 20 { + d.logReset() + } + d.enableInterrupts(true) +} + +//go:noinline +func (d *dhw) logReset() { + a = d.logCount +} + +func deleteCache(addr, size uintptr) {} +func flushCache(addr, size uintptr) {} +func runBootloader() {} // allocDHW returns a reference to the USB hardware abstraction for the given // device controller driver. Should be called only one time and during device @@ -52,13 +76,13 @@ func allocDHW(port, instance int, speed Speed, dc *dcd) *dhw { func(interrupt.Interrupt) { coreInstance[0].dc.interrupt() }) dhwInstance[instance].irqSOF = interrupt.New(sam.IRQ_USB_SOF_HSOF, - func(interrupt.Interrupt) { coreInstance[0].dc.startOfFrame() }) + func(interrupt.Interrupt) { coreInstance[0].dc.interrupt() }) dhwInstance[instance].irqTC0 = interrupt.New(sam.IRQ_USB_TRCPT0, - func(interrupt.Interrupt) { coreInstance[0].dc.complete(0) }) + func(interrupt.Interrupt) { coreInstance[0].dc.interrupt() }) dhwInstance[instance].irqTC1 = interrupt.New(sam.IRQ_USB_TRCPT1, - func(interrupt.Interrupt) { coreInstance[0].dc.complete(1) }) + func(interrupt.Interrupt) { coreInstance[0].dc.interrupt() }) } // SAMx51 has only one USB PHY, which is full-speed @@ -66,6 +90,15 @@ func allocDHW(port, instance int, speed Speed, dc *dcd) *dhw { speed = FullSpeed } dhwInstance[instance].speed = speed + dhwInstance[instance].ready = false + + // initialize the transfer descriptors + for i := range dhwInstance[instance].ep { + dhwInstance[instance].ep[i][descBankOut].init( + &dhwInstance[instance], rxEndpoint(uint8(i))) + dhwInstance[instance].ep[i][descBankIn].init( + &dhwInstance[instance], txEndpoint(uint8(i))) + } return &dhwInstance[instance] } @@ -131,10 +164,21 @@ func (d *dhw) calibrate() { func (d *dhw) init() status { // Enable USB clocks - const clockGenerator = sam.GCLK_PCHCTRL_GEN_GCLK10 + // const clockGenerator = sam.GCLK_PCHCTRL_GEN_GCLK10 + const clockGenerator = sam.PCHCTRL_GCLK_USB sam.MCLK.APBBMASK.SetBits(sam.MCLK_APBBMASK_USB_) sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_USB_) - sam.GCLK.PCHCTRL[clockGenerator].Set(clockGenerator | sam.GCLK_PCHCTRL_CHEN) + sam.GCLK.PCHCTRL[clockGenerator].Set( + (sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) | + sam.GCLK_PCHCTRL_CHEN) + + // Reset USB peripheral + for d.bus.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) { + } + d.bus.CTRLA.Set(sam.USB_DEVICE_CTRLA_SWRST) + for d.bus.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) { + } + d.calibrate() // Initialize USB interrupt priorities d.irqEVT.SetPriority(dhwInterruptPriority) @@ -148,14 +192,6 @@ func (d *dhw) init() status { sam.IRQ_USB_TRCPT0|sam.IRQ_USB_TRCPT1) arm.EnableInterrupts(m) - // Reset USB peripheral - d.bus.CTRLA.Set(sam.USB_DEVICE_CTRLA_SWRST) - for !d.bus.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) { - } - for d.bus.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) { - } - d.calibrate() - // USB Quality of Service: High Quality (3) d.bus.QOSCTRL.Set((3 << sam.USB_DEVICE_QOSCTRL_CQOS_Pos) | (3 << sam.USB_DEVICE_QOSCTRL_DQOS_Pos)) @@ -164,9 +200,9 @@ func (d *dhw) init() status { var addr uintptr switch d.cc.id { case classDeviceCDCACM: - addr = uintptr(unsafe.Pointer(descCDCACM[d.cc.config-1].ed)) + addr = uintptr(unsafe.Pointer(&descCDCACM[d.cc.config-1].ed[0])) case classDeviceHID: - addr = uintptr(unsafe.Pointer(descHID[d.cc.config-1].ed)) + addr = uintptr(unsafe.Pointer(&descHID[d.cc.config-1].ed[0])) } d.bus.DESCADD.Set(uint32(addr)) @@ -180,82 +216,255 @@ func (d *dhw) init() status { // Clear and enable interrupts in USB core d.bus.INTFLAG.Set(d.bus.INTFLAG.Get()) - d.bus.INTENSET.Set(sam.USB_DEVICE_INTENSET_SOF | sam.USB_DEVICE_INTENSET_EORST) + d.bus.INTENSET.Set( /*sam.USB_DEVICE_INTENSET_SOF |*/ + sam.USB_DEVICE_INTENSET_EORST) // Ensure D+ pulled down long enough for host to detect a previous disconnect udelay(5000) + d.ready = true + return statusOK } -// enable causes the USB core to enter (or exit) the normal run state and -// enables/disables all interrupts on the receiver's USB port. +// enable enables the USB interrupts, connects the device to the bus via +// internal D+/D- pullup resistors, and enters the normal runtime. func (d *dhw) enable(enable bool) { - d.enableInterrupts(enable) + if d.ready { // ensure init() has been called + d.enableInterrupts(enable) + d.connect(enable) + } +} + +// connect attaches the USB device by enabling/disabling the internal pullup +// resistor on D+/D-. +func (d *dhw) connect(connect bool) { + if d.ready { // ensure init() has been called + if connect { + d.bus.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) + } else { + d.bus.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH) + } + } } // enableInterrupts enables/disables all interrupts on the receiver's USB port. func (d *dhw) enableInterrupts(enable bool) { - if enable { - d.irqEVT.Enable() // Enable USB interrupts - d.irqSOF.Enable() - d.irqTC0.Enable() - d.irqTC1.Enable() - } else { - d.irqEVT.Disable() // Disable USB interrupts - d.irqSOF.Disable() - d.irqTC0.Disable() - d.irqTC1.Disable() + if d.ready { // ensure init() has been called + if enable { + d.irqEVT.Enable() // Enable USB interrupts + d.irqSOF.Enable() + d.irqTC0.Enable() + d.irqTC1.Enable() + } else { + d.irqEVT.Disable() // Disable USB interrupts + d.irqSOF.Disable() + d.irqTC0.Disable() + d.irqTC1.Disable() + } } } // enableSOF enables or disables start-of-frame (SOF) interrupts on the given // USB device interface. func (d *dhw) enableSOF(enable bool, iface uint8) { + // if changing enabled state, clear interrupt + if enable != d.bus.INTENSET.HasBits(sam.USB_DEVICE_INTENSET_SOF) { + d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_SOF) + } + if enable { + d.bus.INTENSET.Set(sam.USB_DEVICE_INTENSET_SOF) + } else { + d.bus.INTENCLR.Set(sam.USB_DEVICE_INTENCLR_SOF) + } } -// interrupt handles the USB hardware interrupt events on the "OTHER" IRQ line -// and notifies the device controller driver using a common "virtual interrupt" +// interrupt handles the USB hardware interrupt events on all four IRQ lines and +// notifies the device controller driver using a common "virtual interrupt" // code. func (d *dhw) interrupt() { - // read and clear the interrupts that fired - status := d.bus.USBSTS.Get() & d.bus.USBINTR.Get() - d.bus.USBSTS.Set(status) + status := d.bus.INTFLAG.Get() & d.bus.INTENSET.Get() + + if status&sam.USB_DEVICE_INTFLAG_SOF != 0 { + d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_SOF) + + // TBD: handle SOF? + } + + // SAMD doesn't distinguish between SUSPEND and DISCONNECT states. + // Both conditions will trigger the SUSPEND interrupt. + // To prevent it triggering when D+/D- are not stable, the SUSPEND interrupt is + // only enabled after receiving SET_ADDRESS request and is cleared on RESET. + if status&sam.USB_DEVICE_INTFLAG_SUSPEND != 0 { + d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_SUSPEND) + + d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_WAKEUP) + d.bus.INTENSET.Set(sam.USB_DEVICE_INTENSET_WAKEUP) + + d.event(dcdEvent{id: dcdEventStatusSuspend}) + } + + if status&sam.USB_DEVICE_INTFLAG_WAKEUP != 0 { + d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_WAKEUP) + d.bus.INTENCLR.Set(sam.USB_DEVICE_INTENCLR_WAKEUP) + + d.event(dcdEvent{id: dcdEventStatusResume}) + } + + if status&sam.USB_DEVICE_INTFLAG_EORST != 0 { + d.bus.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST) + d.bus.INTENCLR.Set(sam.USB_DEVICE_INTENCLR_WAKEUP | + sam.USB_DEVICE_INTENCLR_SUSPEND) + + d.event(dcdEvent{id: dcdEventDeviceReady}) + } + + num := endpointNumber(d.controlEndpoint()) + if d.bus.DEVICE_ENDPOINT[num].EPINTFLAG.HasBits( + sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP) { + d.bus.DEVICE_ENDPOINT[num].EPINTFLAG.Set( + sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP | + sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) + + d.logEvent("setup received") + + // Parse the SETUP packet immediately, clearing room in the (one and only) + // control buffer for the next SETUP packet received. + sup := setupFrom(d.controlSetupBuffer()) + dir := sup.direction() + d.prepareSetup() + + // Although there is only one control buffer, EP0 has two transfer queues: + // 1×Rx(OUT) and 1×Tx(IN). First we decode the SETUP packet via setupFrom, + // and based on its contained request's direction (IN vs OUT), we attempt + // to enqueue a new transfer request in EP0's corresponding transfer queue. + if ready, _ := d.ep[num][dir].scheduleSetup(sup); ready { + // Begin processing the control packet immediately since there were no + // pending transfers in the control EP0's IN/OUT transfer queue. + d.controlTransferStart(packEndpoint(num, dir)) + } else { + // The EP0 IN/OUT transfer queue is busy servicing a previous request. + // Stall the endpoint. + d.controlStall(true, dir) + } + } + + // maybe transfer complete + + epints := d.bus.EPINTSMRY.Get() + + for ep := uint8(0); ep < descMaxEndpoints; ep++ { + if (epints & (1 << ep)) == 0 { + continue + } + + intFlag := d.bus.DEVICE_ENDPOINT[ep].EPINTFLAG.Get() + + out, in := d.endpointDescriptors(ep) + + // handle Tx (IN) endpoint complete + if intFlag&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1 != 0 { + pcksize := in.packetSize.Get() + // number of bytes to be sent on next IN transaction + count := (pcksize >> USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & + USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk + // total number of bytes sent + total := (pcksize >> USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) & + USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk + // maximum packet size + size, _ := endpointSizeDecode((pcksize >> USB_DEVICE_PCKSIZE_SIZE_Pos) & + USB_DEVICE_PCKSIZE_SIZE_Msk) + + d.bus.DEVICE_ENDPOINT[ep].EPINTFLAG.Set( + sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) + d.controlStall(false, descDirIn) + + if ep == d.controlEndpoint() { + d.logEvent("EP0 Tx packet complete") + // check if there is more data to transfer or if we need to notify the + // upper-layer device driver of a control transfer completion event. + if count == 0 || count < size { + d.logEvent("EP0 Tx transfer complete") + d.controlTransferComplete(txEndpoint(ep), count, total) + } else { + d.controlTransferContinue(txEndpoint(ep), count, total) + } + } else if nil != d.ep[ep][descBankIn].callback { + // call our device class-specific callback, if defined, on endpoint + // data transfer complete events. + d.ep[ep][descBankIn].callback(txEndpoint(ep), count) + } + } + + // handle Rx (OUT) endpoint complete + if intFlag&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0 != 0 { + pcksize := out.packetSize.Get() + // number of bytes received on last OUT/SETUP transaction + count := (pcksize >> USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & + USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk + // total data size for the complete transfer + total := (pcksize >> USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) & + USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk + // maximum packet size + size, _ := endpointSizeDecode((pcksize >> USB_DEVICE_PCKSIZE_SIZE_Pos) & + USB_DEVICE_PCKSIZE_SIZE_Msk) + + d.bus.DEVICE_ENDPOINT[ep].EPINTFLAG.Set( + sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) + d.controlStall(false, descDirOut) + + if ep == d.controlEndpoint() { + d.logEvent("EP0 Rx packet complete") + // check if there is more data to transfer or if we need to notify the + // upper-layer device driver of a control transfer completion event. + if count == 0 || count < size { + d.logEvent("EP0 Rx transfer complete") + d.controlTransferComplete(rxEndpoint(ep), count, total) + } else { + d.controlTransferContinue(rxEndpoint(ep), count, total) + } + } else if nil != d.ep[ep][descBankOut].callback { + // call our device class-specific callback, if defined, on endpoint + // data transfer complete events. + d.ep[ep][descBankOut].callback(rxEndpoint(ep), count) + } + } + } } -// startOfFrame handles the USB hardware interrupt events on the "SOF_HSOF" IRQ -// lines and notifies the device controller driver using a common "virtual -// interrupt" code. -func (d *dhw) startOfFrame() { -} - -// complete handles the USB hardware interrupt events on the "TRCPT0" and -// "TRCPT1" IRQ lines and notifies the device controller driver using a common -// "virtual interrupt" code. -// -// When bank is 0, the interrupt occurred on "TRCPT0". Otherwise, bank is 1, and -// the interrupt occurred on "TRCPT1". -func (d *dhw) complete(bank int) { -} - +// prepareSetup configures the buffer for setup packets received on control +// endpoint 0 Rx (OUT). func (d *dhw) prepareSetup() { - - // Configure control endpoint 0 OUT only - endpoint := d.controlEndpoint() - out, _ := d.endpointAddressDescriptor(endpoint) - - out.address.Set(uint32(d.controlBuffer())) - out.packetSize.ReplaceBits( - pcksize(0, 0, uint32(dcdSetupSize)), - (USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk< 0 && data > 0 { + if xfer, ok := d.ep[ep][descBankOut].activeTransfer(); ok { + next := xfer.packetStart(data, size) + d.endpointTransfer(rxEndpoint(ep), data, next) + } + } else { + d.endpointTransfer(txEndpoint(ep), 0, 0) + } +} + +// controlTransmit transmits (Tx, IN) the first data packet on control endpoint 0. +// If the given data pointer and size are both 0, then a zero-length status +// packet (ZLP) is received (Rx, OUT) on control endpoint 0. +func (d *dhw) controlTransmit(data uintptr, size uint32, notify bool) { + ep := d.controlEndpoint() + if size > 0 && data > 0 { + if xfer, ok := d.ep[ep][descBankIn].activeTransfer(); ok { + next := xfer.packetStart(data, size) + d.endpointTransfer(txEndpoint(ep), data, next) + } + } else { + d.endpointTransfer(rxEndpoint(ep), 0, 0) + } +} + +// ============================================================================= +// Endpoint Transfer Descriptor +// ============================================================================= + +type dhwTransfer struct { + endpoint uint8 + maxPacketSize uint32 + setup dcdSetup + data uintptr + size uint32 + sent uint32 +} + +// dhwTransferDepth defines the size of the dhwEPStatus.xferQueue buffered channel, +// which affects the number of transfers each endpoint can enqueue for processing. +// const dhwTransferDepth = 8 + +type dhwTransferLUT [QueueSize]dhwTransfer + +func (t *dhwTransfer) init(endpoint uint8, maxPacketSize uint32) { + t.endpoint = endpoint + t.maxPacketSize = maxPacketSize + t.reset() +} + +func (t *dhwTransfer) reset() { + // Do not clear the endpoint field, as it is statically-assigned (during + // program initialization) and is never intended to change. + t.setup.set(0) + t.data = 0 + t.size = 0 + t.sent = 0 +} + +func (t *dhwTransfer) packetStart(data uintptr, size uint32) (next uint32) { + t.data = data + t.size = size + t.sent = 0 + if next = size; next > t.maxPacketSize { + next = t.maxPacketSize + } + return next +} + +func (t *dhwTransfer) packetComplete(sent uint32) (data uintptr, size uint32) { + t.sent = sent + if size = t.size - t.sent; size > t.maxPacketSize { + size = t.maxPacketSize + } + return t.data + uintptr(t.sent), size +} + +func (t *dhwTransfer) hasDataPayload() bool { return t.data != 0 || t.size != 0 } +func (t *dhwTransfer) hasSetupPayload() bool { return t.setup.pack() != 0 } +func (t *dhwTransfer) hasPayload() bool { return t.hasDataPayload() || t.hasSetupPayload() } + +// ============================================================================= +// Endpoint Configuration and Status +// ============================================================================= + +// dhwEPStatus holds the status, completion callback of the configured class +// driver, and the transfer queue for a given endpoint. +// +// The transfer queue is structured as follows: +// +// - The xferTable field is a statically-allocated, single-dimensional array +// used as a buffer of transfer requests - known as transfer descriptors - +// on a single, directional endpoint. +// +// - The length of xferTable defines the maximum number of pending transfers +// in a given direction on a single endpoint. +// +// - Since the transfer descriptors are statically-allocated, we do not risk +// heap allocation when requesting transfers in the USB interrupt handler. +// +// - The xferQueue field is a buffered channel of uint8 with capacity equal to +// the length of the transfer descriptor table xferTable. +// +// - To enqueue a new transfer request, the xferTable is first scanned to find +// the index of an unused transfer descriptor. The descriptor at this table +// index is populated with the transfer details, and this table index is +// written to the xferQueue channel. +// +// - If no other transfer descriptors are queued, the transfer is immediatly +// sent to the USB. Otherwise, the next descriptor in queue will be read +// from the xferQueue channel upon the next transfer complete interrupt +// triggered on this endpoint. +// +// - Once the transfer descriptor's table index is read from the xferQueue +// channel, the descriptor is cleared in the xferTable, marking it free for +// use with a subsequent transfer request. +// +type dhwEPStatus struct { + device *dhw + endpoint uint8 + callback func(endpoint uint8, size uint32) + flags volatile.Register8 + xferActive volatile.Register32 + xferQueue Queue + xferTable dhwTransferLUT +} + +// dhwEPAddrStatus contains an endpoint number's dhwEPStatus for both IN + OUT +// directions. +type dhwEPAddrStatus [2]dhwEPStatus + +// Bitmasks for each bitfield stored in volatile field dhwEPStatus.flags. +const ( + dhwEPStatusStatusBusy = 0x1 + dhwEPStatusStatusStalled = 0x2 + dhwEPStatusStatusClaimed = 0x4 +) + +func (s *dhwEPStatus) init(dhw *dhw, endpoint uint8) { + s.device = dhw + s.endpoint = endpoint + s.callback = nil + s.flags.Set(0) + s.xferQueue.Reset() + mps := dhw.endpointMaxPacketSize(endpoint) + for i := range s.xferTable { + s.xferTable[i].init(endpoint, mps) + } +} + +// Accessor methods to return the logical boolean value from the bit value +// stored in volatile field dhwEPStatus.flags. +func (s *dhwEPStatus) busy() bool { return s.flags.HasBits(dhwEPStatusStatusBusy) } +func (s *dhwEPStatus) stalled() bool { return s.flags.HasBits(dhwEPStatusStatusStalled) } +func (s *dhwEPStatus) claimed() bool { return s.flags.HasBits(dhwEPStatusStatusClaimed) } + +// Mutator methods to set the bit value from the logical boolean value stored in +// volatile field dhwEPStatus.flags. +func (s *dhwEPStatus) setBusy(set bool) { s.setFlags(set, dhwEPStatusStatusBusy) } +func (s *dhwEPStatus) setStalled(set bool) { s.setFlags(set, dhwEPStatusStatusStalled) } +func (s *dhwEPStatus) setClaimed(set bool) { s.setFlags(set, dhwEPStatusStatusClaimed) } + +// setFlags consolidates the common logic of each dhwEPStatus mutator method +// defined above. +func (s *dhwEPStatus) setFlags(set bool, mask uint8) { + if set { + s.flags.SetBits(mask) + } else { + s.flags.ClearBits(mask) + } +} + +// hasActiveTransfer returns true if and only if the receiver's active transfer +// descriptor is not nil. +// +// Note that the result of this call does not guarantee a subsequent call to +// activeTransfer will succeed, as the active transfer may have been cleared +// preemptively (from the USB interrupt handler) during the time between these +// two calls. Thus, you should always verify an active transfer descriptor was +// obtained with the bool value returned from activeTransfer. +func (s *dhwEPStatus) hasActiveTransfer() bool { + _, ok := s.activeTransfer() + return ok +} + +// activeTransfer returns a pointer to the receiver's active transfer descriptor +// being processed in one of the transaction stages (SETUP, DATA, or STATUS). +// The bool value returned is true if and only if the receiver's active transfer +// descriptor is not nil. +// +// The pointer returned refers to an element in the receiver's xferTable, which +// is also used by the receiver's pending transfer queue (FIFO). Thus, you can +// (and should) use this object to reset transfer descriptors when processing +// has completed (using (*dhwTransfer).reset()). This frees the descriptor and +// allows new transfer requests to be scheduled. +// You may also use (*dhwEPStatus).setActiveTransfer(nil) to free the descriptor +// if the receiver's active transfer descriptor is not nil. +func (s *dhwEPStatus) activeTransfer() (*dhwTransfer, bool) { + if active := s.xferActive.Get(); active != 0 { + return (*dhwTransfer)(unsafe.Pointer(uintptr(active))), true + } + return nil, false +} + +// setActiveTransfer sets or clears the receiver's active transfer descriptor. +// The receiver's active transfer descriptor is cleared if the given transfer +// descriptor is nil. +// +// If the given transfer descriptor is nil, and the receiver's active transfer +// descriptor is not nil, then the receiver's active transfer descriptor is +// reset, marking it free for use by the receiver's transfer queue (FIFO). +// +// The given transfer descriptor should be a pointer into the receiver's +// transfer table xferTable. This enables interaction with the receiver's +// transfer queue, allowing it to detect when a descriptor is busy or available +// for scheduling. +func (s *dhwEPStatus) setActiveTransfer(xfer *dhwTransfer) { + if xfer == nil { + // Clearing the active transfer. Check if an active descriptor exists. + if actv, ok := s.activeTransfer(); ok { + // Reset the descriptor, freeing it for use in the transfer queue (FIFO). + actv.reset() + } + s.xferActive.Set(0) + } else { + s.xferActive.Set(uint32(uintptr(unsafe.Pointer(xfer)))) + } +} + +// hasPendingTransfer returns true if and only if the number of pending +// transfers in the receiver's transfer queue is greater than zero. +// +// Note that the result of this call does not guarantee that calls to either +// pendingTransfer/scheduleSetup/scheduleTransfer will succeed, as new requests +// may be added/removed preemptively (from the USB interrupt handler) during the +// time between these two calls. Thus, you should always verify queue operations +// operations by inspecting the final bool value returned by each of these +// mentioned functions. +func (s *dhwEPStatus) hasPendingTransfer() bool { + return s.xferQueue.Len() > 0 +} + +// pendingTransfer dequeues the table index - referring to the next transfer +// descriptor to be processed - from the receiver's xferQueue, returning the +// transfer descriptor at that index and true to indicate a pending transfer +// descriptor was successfully obtained. +// +// If the receiver's transfer queue is empty, then the returned values are nil +// and a false bool value to indicate failure to obtain a pending transfer +// descriptor. +func (s *dhwEPStatus) pendingTransfer() (*dhwTransfer, bool) { + if s.hasPendingTransfer() { + s.device.enableInterrupts(false) + defer s.device.enableInterrupts(true) + if i, ok := s.xferQueue.Deq(); ok { + return &s.xferTable[i], true + } + } + return nil, false +} + +// claimSchedule disables interrupts and scans the receiver's transfer table +// for an unused transfer descriptor, returning its table index and true. +// If all transfer descriptors are already claimed, re-enables interrupts and +// returns -1 and false. +// +// -- ** IMPORTANT ** -- +// Note that interrupts are NOT re-enabled when a transfer index is +// successfully found and returned. This ensures no race condition exists +// between locating a free transfer index and initializing the transfer at that +// index. These two events must not be preempted by another scheduling request +// from the USB interrupt handler. +// The caller must re-enable interrupts once the available transfer at the +// vacant index has been processed. +// +// ( Because of this potentially danerous behavior, claimSchedule should be +// restricted to the scheduling methods — scheduleTransfer and scheduleSetup — +// so it can be verified easily that interrupts get re-enabled in all cases. ) +func (s *dhwEPStatus) claimSchedule() (int, bool) { + // Disable interrupts while scanning the xferTable + s.device.enableInterrupts(false) + for i := range s.xferTable { + // Check that transfer has no payloads + if !s.xferTable[i].hasPayload() { + // Return index into xferTable (leave interrupts disabled!) + return i, true + } + } + // All elements of xferTable have a payload, so we cannot schedule a new + // transfer. This request will be ignored, and we can re-enable interrupts + // immediately. + // + // Realistically, we should never encounter this condition with a + // sufficiently-sized xferTable/xferQueue and a well-behaved USB host. + // + // If you do reach this point, check that the transfers are being cleaned + // up properly (with (*dhwTransfer).reset()) in the respective transfer + // completion event handler. + s.device.enableInterrupts(true) + return -1, false +} + +// scheduleTransfer enqueues a new data transfer descriptor to the receiver's +// transfer queue. +// +// The first bool returned indicates if this transfer request is the the only +// request in the queue, no other active transfer exists, and is thus available +// for immediate processing. +// The second bool returned is true if and only if the transfer request was +// added to the queue successfully. +// If the receiver's transfer queue is full, the request is ignored and false is +// returned for both return values. +func (s *dhwEPStatus) scheduleTransfer(data uintptr, size uint32) (ready bool, ok bool) { + var i int + if i, ok = s.claimSchedule(); ok { + defer s.device.enableInterrupts(true) + s.xferTable[i].reset() + s.xferTable[i].data = data + s.xferTable[i].size = size + return !s.hasActiveTransfer() && !s.hasPendingTransfer(), + s.xferQueue.Enq(uint8(i)) + } + return false, false +} + +// scheduleSetup enqueues a new control SETUP transfer to the receiver's +// transfer queue. +// +// The first bool returned indicates if this transfer request is the the only +// request in the queue, no other active transfer exists, and is thus available +// for immediate processing. +// The second bool returned is true if and only if the transfer request was +// added to the queue successfully. +// If the receiver's transfer queue is full, the request is ignored and false is +// returned for both return values. +func (s *dhwEPStatus) scheduleSetup(setup dcdSetup) (ready bool, ok bool) { + var i int + s.device.logEvent("setup queued") + if i, ok = s.claimSchedule(); ok { + defer s.device.enableInterrupts(true) + s.xferTable[i].reset() + s.xferTable[i].setup = setup + return !s.hasActiveTransfer() && !s.hasPendingTransfer(), + s.xferQueue.Enq(uint8(i)) + } + return false, false } // ============================================================================= // Endpoint Descriptor // ============================================================================= -// dhwEndptDesc defines a USB endpoint descriptor, used to inform the USB DMA +// dhwEPDesc defines a USB endpoint descriptor, used to inform the USB DMA // controller the location of each endpoint transfer buffer. -type dhwEndptDesc struct { +// +// Access to these instances is controlled; i.e., you shouldn't need to use +// them directly. Instead, use the higher-level API on types dhwEPStatus and +// dhwTransfer, through the (*dhw).ep[num][dir] elements, for scheduling and +// inspecting endpoint transfers. +type dhwEPDesc struct { address volatile.Register32 packetSize volatile.Register32 extToken volatile.Register16 @@ -352,13 +985,9 @@ type dhwEndptDesc struct { _ [5]uint8 } -// dhwEndptAddrDesc defines an endpoint address descriptor, representing both +// dhwEPAddrDesc defines an endpoint address descriptor, representing both // directions (IN + OUT) of a given endpoint descriptor. -type dhwEndptAddrDesc [2]dhwEndptDesc - -// dhwEndptDescSize defines the size (bytes) of a structure containing a USB -// endpoint descriptor. -const dhwEndptDescSize = unsafe.Sizeof(dhwEndptDesc{}) // 16 bytes +type dhwEPAddrDesc [2]dhwEPDesc // Constants defining bitfields in the endpoint descriptor hardware register // PCKSIZE. These were left out of the SVD for some reason. @@ -366,28 +995,48 @@ const ( USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0 USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk = 0x3FFF + USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14 + USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk = 0x3FFF + USB_DEVICE_PCKSIZE_SIZE_Pos = 28 USB_DEVICE_PCKSIZE_SIZE_Msk = 0x7 - USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14 - USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk = 0x3FFF + USB_DEVICE_PCKSIZE_AUTOZLP_Pos = 31 + USB_DEVICE_PCKSIZE_AUTOZLP_Msk = 0x1 ) -func pcksize(byteCount, size, multiPacketSize uint32) uint32 { - return ((byteCount & USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk) << USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos) | +// pcksize is a convenience routine that constructs the bitfields of the PCKSIZE +// register of the USB_DEVICE peripheral, whose Pos/Msk definitions were ommitted +// from the SVD-generated device file. +//go:inline +func pcksize(byteCount, multiPacketSize, size uint32, zlp bool) uint32 { + var zlpMask uint32 + if zlp { + zlpMask = USB_DEVICE_PCKSIZE_AUTOZLP_Msk << USB_DEVICE_PCKSIZE_AUTOZLP_Pos + } + return ((byteCount & USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk) << + USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos) | + ((multiPacketSize & USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk) << + USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) | ((size & USB_DEVICE_PCKSIZE_SIZE_Msk) << USB_DEVICE_PCKSIZE_SIZE_Pos) | - ((multiPacketSize & USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk) << USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) + (zlpMask) } var ( // endpointSizeEnum is a constant-time lookup table for translating packet // sizes (bytes) to the corresponding register PCKSIZE.SIZE enumerated value. + // + // These tables are used instead of simple arithmetic (powers of 2) because of + // the exceptional case with packet size = 1023. endpointSizeEnum = map[uint32]uint32{ 8: 0, 16: 1, 32: 2, 64: 3, 128: 4, 256: 5, 512: 6, 1023: 7, } // endpointEnumSize is a constant-time lookup table for translating the // register PCKSIZE.SIZE enumerated values to its packet size (bytes). + // + // These tables are used instead of simple arithmetic (powers of 2) because of + // the exceptional case with packet size = 1023. endpointEnumSize = [8]uint32{ /* 0= */ 8, /* 1= */ 16, @@ -405,6 +1054,7 @@ var ( // // See documentation on endpoint descriptor bank SRAM register PCKSIZE, bit // field SIZE for details. +//go:inline func endpointSizeEncode(size uint32) (enum uint32, ok bool) { enum, ok = endpointSizeEnum[size] return @@ -415,6 +1065,7 @@ func endpointSizeEncode(size uint32) (enum uint32, ok bool) { // // See documentation on endpoint descriptor bank SRAM register PCKSIZE, bit // field SIZE for details. +//go:inline func endpointSizeDecode(enum uint32) (size uint32, ok bool) { if ok = int(enum) < len(endpointEnumSize); ok { size = endpointEnumSize[enum] @@ -422,30 +1073,21 @@ func endpointSizeDecode(enum uint32) (size uint32, ok bool) { return } -// endpointAddressDescriptor returns the IN+OUT endpoint descriptors for the -// given endpoint address, encoded as direction D and endpoint number N with the -// 8-bit mask DxxxNNNN. The direction bit D is ignored. +// endpointDescriptors returns the OUT + IN endpoint descriptors for the given +// endpoint number, encoded as direction D and endpoint number N with the 8-bit +// mask D000NNNN. The direction bit D is ignored. //go:inline -func (d *dhw) endpointAddressDescriptor(endpoint uint8) (out, in *dhwEndptDesc) { +func (d *dhw) endpointDescriptors(endpoint uint8) (out, in *dhwEPDesc) { // endpoint descriptor is device class-specific - num, _ := unpackEndpoint(endpoint) - switch d.cc.id { - case classDeviceCDCACM: - return &descCDCACM[d.cc.config-1].ed[num][descBankOut], - &descCDCACM[d.cc.config-1].ed[num][descBankIn] - case classDeviceHID: - return &descHID[d.cc.config-1].ed[num][descBankOut], - &descHID[d.cc.config-1].ed[num][descBankIn] - default: - return nil, nil - } + return d.endpointDescriptor(rxEndpoint(endpoint)), + d.endpointDescriptor(txEndpoint(endpoint)) } // endpointDescriptor returns the endpoint descriptor for the given endpoint // address, encoded as direction D and endpoint number N with the 8-bit mask -// DxxxNNNN. +// D000NNNN. //go:inline -func (d *dhw) endpointDescriptor(endpoint uint8) *dhwEndptDesc { +func (d *dhw) endpointDescriptor(endpoint uint8) *dhwEPDesc { // endpoint descriptor is device class-specific num, dir := unpackEndpoint(endpoint) switch d.cc.id { @@ -458,224 +1100,242 @@ func (d *dhw) endpointDescriptor(endpoint uint8) *dhwEndptDesc { } } -type dhwEndpointRegister int +func (d *dhw) endpointMaxPacketSize(endpoint uint8) uint32 { -const ( - epRegNone dhwEndpointRegister = iota - epRegConfig - epRegStatusClr - epRegStatusSet - epRegStatus - epRegIntFlag - epRegIntEnClr - epRegIntEnSet -) + switch d.cc.id { + case classDeviceCDCACM: -func (d *dhw) endpointRegister(endpoint uint8, - register dhwEndpointRegister) *volatile.Register8 { + switch endpointNumber(endpoint) { + case descCDCACMEndpointCtrl: + return descControlPacketSize - if num, _ := unpackEndpoint(endpoint); num < descMaxEndpoints { + case descCDCACMEndpointStatus: + return descCDCACMStatusPacketSize - switch register { - case epRegConfig: - return &d.bus.DEVICE_ENDPOINT[num].EPCFG + case descCDCACMEndpointDataRx: + return descCDCACMDataRxPacketSize - case epRegStatusClr: - return &d.bus.DEVICE_ENDPOINT[num].EPSTATUSCLR + case descCDCACMEndpointDataTx: + return descCDCACMDataTxPacketSize + } + case classDeviceHID: - case epRegStatusSet: - return &d.bus.DEVICE_ENDPOINT[num].EPSTATUSSET + switch endpointNumber(endpoint) { + case descHIDEndpointCtrl: + return descControlPacketSize - case epRegStatus: - return &d.bus.DEVICE_ENDPOINT[num].EPSTATUS + case descHIDEndpointKeyboard: + return descHIDKeyboardTxPacketSize - case epRegIntFlag: - return &d.bus.DEVICE_ENDPOINT[num].EPINTFLAG + case descHIDEndpointMouse: + return descHIDMouseTxPacketSize - case epRegIntEnClr: - return &d.bus.DEVICE_ENDPOINT[num].EPINTENCLR + case descHIDEndpointSerialRx: // == descHIDEndpointSerialTx + switch endpoint { + case rxEndpoint(endpoint): + return descHIDSerialRxPacketSize + case txEndpoint(endpoint): + return descHIDSerialTxPacketSize + } - case epRegIntEnSet: - return &d.bus.DEVICE_ENDPOINT[num].EPINTENSET + case descHIDEndpointJoystick: + return descHIDJoystickTxPacketSize + + case descHIDEndpointMediaKey: + return descHIDMediaKeyTxPacketSize } } - return nil + return descControlPacketSize } func (d *dhw) endpointEnable(endpoint uint8, control bool, config uint32) { + if control { + + // Configure control endpoint 0 Rx (bank 0, OUT) and Tx (bank 1, IN) + out, in := d.endpointDescriptors(d.controlEndpoint()) + + if enum, ok := endpointSizeEncode(descControlPacketSize); ok { + + // Conigure packet size for control endpoints. + out.packetSize.ReplaceBits(enum, + USB_DEVICE_PCKSIZE_SIZE_Msk, USB_DEVICE_PCKSIZE_SIZE_Pos) + in.packetSize.ReplaceBits(enum, + USB_DEVICE_PCKSIZE_SIZE_Msk, USB_DEVICE_PCKSIZE_SIZE_Pos) + + num := endpointNumber(d.controlEndpoint()) + + // rxType/txType uses the same rationale as epType (defined below in the + // else-branch that handles non-control endpoints). + // Thus, we add +1 to the value below. + // + // See the comment above the previously-mentioned epType (below) + rxType := uint8(descEndptTypeControl+1) << + sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos + txType := uint8(descEndptTypeControl+1) << + sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos + + // Configure bank 0 Rx (SETUP/OUT) as CONTROL, bank 1 Tx (IN) as CONTROL. + d.bus.DEVICE_ENDPOINT[num].EPCFG.Set(rxType | txType) + // Enable transfer complete and SETUP received interrupts + d.bus.DEVICE_ENDPOINT[num].EPINTENSET.Set( + sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0 | + sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1 | + sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP) + + // Prepare to start processing SETUP packets + d.prepareSetup() + } + + } else { + + desc := d.endpointDescriptor(endpoint) + + if enum, ok := endpointSizeEncode(d.endpointMaxPacketSize(endpoint)); ok { + + desc.packetSize.ReplaceBits(enum, + USB_DEVICE_PCKSIZE_SIZE_Msk, USB_DEVICE_PCKSIZE_SIZE_Pos) + + num := endpointNumber(endpoint) + + // config contains the bmAttributes field per USB standard EP descriptor, + // i.e., ctrl=0, iso=1, bulk=2, int=3, which corresponds to the EPCFG + // register's EPTYPE0/1 bitfield+1: ctrl=1, iso=2, bulk=3, int=4, dual=5. + // Thus, we add +1 to the value below. + + switch endpoint { + case rxEndpoint(endpoint): + + epType := ((config >> descEndptConfigAttrRxPos) & + descEndptAttrSyncTypeMsk) >> descEndptAttrSyncTypePos + + d.bus.DEVICE_ENDPOINT[num].EPCFG.ReplaceBits( + uint8(epType+1)<> descEndptConfigAttrTxPos) & + descEndptAttrSyncTypeMsk) >> descEndptAttrSyncTypePos + + d.bus.DEVICE_ENDPOINT[num].EPCFG.ReplaceBits( + uint8(epType+1)< descCDCACMEndpointCount { - return - } + // overwrite the BYTE_COUNT and MULTI_PACKET_SIZE bitfields only (with 0 and + // size, respectively). + var mask uint32 + mask |= USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk << + USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos + mask |= USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk << + USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos + desc.packetSize.ReplaceBits(pcksize(0, size, 0, false), mask, 0) - // HID - case classDeviceHID: - if endpoint < descHIDEndpointSerialRx || - endpoint > descHIDEndpointCount { - return - } + d.bus.DEVICE_ENDPOINT[num].EPSTATUSCLR.SetBits( + sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY) + d.bus.DEVICE_ENDPOINT[num].EPINTFLAG.SetBits( + sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRFAIL0) + + case descDirIn: // Tx + + // overwrite the BYTE_COUNT and MULTI_PACKET_SIZE bitfields only (with size + // and 0, respectively). + var mask uint32 + mask |= USB_DEVICE_PCKSIZE_BYTE_COUNT_Msk << + USB_DEVICE_PCKSIZE_BYTE_COUNT_Pos + mask |= USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Msk << + USB_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos + desc.packetSize.ReplaceBits(pcksize(size, 0, 0, false), mask, 0) + + d.bus.DEVICE_ENDPOINT[num].EPSTATUSSET.SetBits( + sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY) + d.bus.DEVICE_ENDPOINT[num].EPINTFLAG.SetBits( + sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRFAIL1) - default: - // Unhandled device class } - -} - -// endpointConfigureTx configures the given bulk data transmit (Tx, IN) endpoint -// for transfer. -func (d *dhw) endpointConfigureTx( - endpoint uint8, packetSize uint16, zlp bool, callback func(transfer *dhwTransfer)) { - - // Configure based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - if endpoint < descCDCACMEndpointStatus || - endpoint > descCDCACMEndpointCount { - return - } - - // HID - case classDeviceHID: - if endpoint < descHIDEndpointSerialRx || - endpoint > descHIDEndpointCount { - return - } - - default: - // Unhandled device class - } - -} - -// endpointReceive schedules a receive (Rx, OUT) transfer on the given endpoint. -func (d *dhw) endpointReceive(endpoint uint8, transfer *dhwTransfer) { - - // Configure based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - if endpoint < descCDCACMEndpointStatus || - endpoint > descCDCACMEndpointCount { - return - } - - // HID - case classDeviceHID: - if endpoint < descHIDEndpointSerialRx || - endpoint > descHIDEndpointCount { - return - } - - default: - // Unhandled device class - } - -} - -// endpointTransmit schedules a transmit (Tx, IN) transfer on the given -// endpoint. -func (d *dhw) endpointTransmit(endpoint uint8, transfer *dhwTransfer) { - - // Configure based on our device class configuration - switch d.cc.id { - - // CDC-ACM (single) - case classDeviceCDCACM: - if endpoint < descCDCACMEndpointStatus || - endpoint > descCDCACMEndpointCount { - return - } - - // HID - case classDeviceHID: - if endpoint < descHIDEndpointSerialRx || - endpoint > descHIDEndpointCount { - return - } - - default: - // Unhandled device class - } - } // endpointComplete handles transfer completion of a data endpoint. -func (d *dhw) endpointComplete(endpoint uint8) { - -} - -// ============================================================================= -// Transfer Descriptor -// ============================================================================= - -// dhwTransfer describes the size and location of data to be transferred to or -// from a USB endpoint. -type dhwTransfer struct { - next *dhwTransfer - token uint32 - pointer [5]uintptr - param uint32 -} - -// dhwTransferSize defines the size (bytes) of a USB standard transfer packet. -const dhwTransferSize = 32 // bytes - -// dhwTransferEOL is a sentinel value used to indicate the final node in a -// linked list of transfer descriptors. -var dhwTransferEOL = (*dhwTransfer)(unsafe.Pointer(uintptr(1))) - -// nextTransfer returns the next transfer descriptor pointed to by the receiver -// transfer descriptor, and whether or not that next descriptor is the final -// descriptor in the list. -func (t dhwTransfer) nextTransfer() (*dhwTransfer, bool) { - return t.next, 1 == uintptr(unsafe.Pointer(t.next)) -} - -func (d *dhw) transferPrepare( - transfer *dhwTransfer, data *uint8, size uint16, param uint32) { - -} - -func (d *dhw) transferSchedule( - endpoint *dhwEndptDesc, mask uint32, transfer *dhwTransfer) { +func (d *dhw) endpointComplete(endpoint uint8, size uint32) { } @@ -713,16 +1373,18 @@ func (d *dhw) uartConfigure() { false, descCDCACMConfigAttrDataRx) d.endpointEnable(descCDCACMEndpointDataTx, false, descCDCACMConfigAttrDataTx) + /* + d.endpointConfigureTx(descCDCACMEndpointStatus, + acm.sxSize, false, nil) + d.endpointConfigureRx(descCDCACMEndpointDataRx, + acm.rxSize, false, d.uartNotify) + d.endpointConfigureTx(descCDCACMEndpointDataTx, + acm.txSize, true, nil) - d.endpointConfigureTx(descCDCACMEndpointStatus, - acm.sxSize, false, nil) - d.endpointConfigureRx(descCDCACMEndpointDataRx, - acm.rxSize, false, d.uartNotify) - d.endpointConfigureTx(descCDCACMEndpointDataTx, - acm.txSize, true, nil) - for i := range acm.rd { - d.uartReceive(uint8(i)) - } + for i := range acm.rd { + d.uartReceive(uint8(i)) + } + */ d.timerConfigure(0, descCDCACMTxSyncUs, d.uartSync) } @@ -735,19 +1397,27 @@ func (d *dhw) uartSetLineCoding(coding descCDCACMLineCoding) { } } +func (d *dhw) uartReady() bool { + acm := &descCDCACM[d.cc.config-1] + _ = acm // TODO(ardnew): elaborate stub + return false +} + func (d *dhw) uartReceive(endpoint uint8) { acm := &descCDCACM[d.cc.config-1] num := uint16(endpoint) & descEndptAddrNumberMsk + _, _ = acm, num // TODO(ardnew): elaborate stub } -func (d *dhw) uartNotify(transfer *dhwTransfer) { +func (d *dhw) uartNotify(endpoint uint8, size uint32) { acm := &descCDCACM[d.cc.config-1] - + _ = acm // TODO(ardnew): elaborate stub } // uartFlush discards all buffered input (Rx) data. func (d *dhw) uartFlush() { acm := &descCDCACM[d.cc.config-1] + _ = acm // TODO(ardnew): elaborate stub } func (d *dhw) uartAvailable() int { @@ -756,6 +1426,8 @@ func (d *dhw) uartAvailable() int { func (d *dhw) uartPeek() (uint8, bool) { acm := &descCDCACM[d.cc.config-1] + _ = acm // TODO(ardnew): elaborate stub + return 0, false } func (d *dhw) uartReadByte() (uint8, bool) { @@ -768,6 +1440,7 @@ func (d *dhw) uartRead(data []uint8) int { acm := &descCDCACM[d.cc.config-1] read := uint16(0) size := uint16(len(data)) + _, _ = acm, size // TODO(ardnew): elaborate stub return int(read) } @@ -779,6 +1452,7 @@ func (d *dhw) uartWrite(data []uint8) int { acm := &descCDCACM[d.cc.config-1] sent := 0 size := len(data) + _, _ = acm, size // TODO(ardnew): elaborate stub return sent } @@ -802,33 +1476,39 @@ func (d *dhw) serialConfigure() { d.endpointEnable(descHIDEndpointSerialRx, false, descHIDConfigAttrSerial) - d.endpointConfigureRx(descHIDEndpointSerialRx, - hid.rxSerialSize, false, d.serialNotify) - d.endpointConfigureTx(descHIDEndpointSerialTx, - hid.txSerialSize, false, nil) - for i := range hid.rdSerial { - d.serialReceive(uint8(i)) - } + // d.endpointConfigureRx(descHIDEndpointSerialRx, + // hid.rxSerialSize, false, d.serialNotify) + // d.endpointConfigureTx(descHIDEndpointSerialTx, + // hid.txSerialSize, false, nil) + + // for i := range hid.rdSerial { + // d.serialReceive(uint8(i)) + // } + d.timerConfigure(0, descHIDSerialTxSyncUs, d.serialSync) } func (d *dhw) serialReceive(endpoint uint8) { hid := &descHID[d.cc.config-1] num := uint16(endpoint) & descEndptAddrNumberMsk + _, _ = hid, num // TODO(ardnew): elaborate stub } func (d *dhw) serialTransmit() { hid := &descHID[d.cc.config-1] + _ = hid // TODO(ardnew): elaborate stub } -func (d *dhw) serialNotify(transfer *dhwTransfer) { - hid := &descHID[d.cc.config-1] - len := hid.rxSerialSize - (uint16(transfer.token>>16) & 0x7FFF) +func (d *dhw) serialNotify( /* transfer *dhwTransfer */ ) { + // hid := &descHID[d.cc.config-1] + // len := hid.rxSerialSize - (uint16(transfer.token>>16) & 0x7FFF) + // _ = len // TODO(ardnew): elaborate stub } // serialFlush discards all buffered input (Rx) data. func (d *dhw) serialFlush() { hid := &descHID[d.cc.config-1] + _ = hid } func (d *dhw) serialSync() { @@ -856,10 +1536,10 @@ func (d *dhw) keyboardConfigure() { d.endpointEnable(descHIDEndpointMediaKey, false, descHIDConfigAttrMediaKey) - d.endpointConfigureTx(descHIDEndpointKeyboard, - hid.txKeyboardSize, false, nil) - d.endpointConfigureTx(descHIDEndpointMediaKey, - hid.txKeyboardSize, false, nil) + // d.endpointConfigureTx(descHIDEndpointKeyboard, + // hid.txKeyboardSize, false, nil) + // d.endpointConfigureTx(descHIDEndpointMediaKey, + // hid.txKeyboardSize, false, nil) } func (d *dhw) keyboardSendKeys(consumer bool) bool { @@ -899,39 +1579,39 @@ func (d *dhw) keyboardSendKeys(consumer bool) bool { func (d *dhw) keyboardWrite(endpoint uint8, data []uint8) bool { - hid := &descHID[d.cc.config-1] + // hid := &descHID[d.cc.config-1] - size := uint16(len(data)) - xfer := &hid.tdKeyboard[hid.txKeyboardHead] - when := ticks() - for { - if 0 == xfer.token&0x80 { - if 0 != xfer.token&0x68 { - // TODO: token contains error, how to handle? - } - hid.txKeyboardPrev = false - break - } - if hid.txKeyboardPrev { - return false - } - if ticks()-when > descHIDKeyboardTxTimeoutMs { - // Waited too long, assume host connection dropped - hid.txKeyboardPrev = true - return false - } - } - // Without this delay, the order packets are transmitted is seriously screwy. - udelay(60) - buff := hid.txKeyboard[hid.txKeyboardHead*descHIDKeyboardTxSize:] - _ = copy(buff, data) - d.transferPrepare(xfer, &buff[0], size, 0) - flushCache(uintptr(unsafe.Pointer(&buff[0])), descHIDKeyboardTxSize) - d.endpointTransmit(endpoint, xfer) - hid.txKeyboardHead += 1 - if hid.txKeyboardHead >= descHIDKeyboardTDCount { - hid.txKeyboardHead = 0 - } + // size := uint16(len(data)) + // xfer := &hid.tdKeyboard[hid.txKeyboardHead] + // when := ticks() + // for { + // if 0 == xfer.token&0x80 { + // if 0 != xfer.token&0x68 { + // // TODO: token contains error, how to handle? + // } + // hid.txKeyboardPrev = false + // break + // } + // if hid.txKeyboardPrev { + // return false + // } + // if ticks()-when > descHIDKeyboardTxTimeoutMs { + // // Waited too long, assume host connection dropped + // hid.txKeyboardPrev = true + // return false + // } + // } + // // Without this delay, the order packets are transmitted is seriously screwy. + // udelay(60) + // buff := hid.txKeyboard[hid.txKeyboardHead*descHIDKeyboardTxSize:] + // _ = copy(buff, data) + // d.transferPrepare(xfer, &buff[0], size, 0) + // flushCache(uintptr(unsafe.Pointer(&buff[0])), descHIDKeyboardTxSize) + // d.endpointTransmit(endpoint, xfer) + // hid.txKeyboardHead += 1 + // if hid.txKeyboardHead >= descHIDKeyboardTDCount { + // hid.txKeyboardHead = 0 + // } return true } @@ -949,8 +1629,8 @@ func (d *dhw) mouseConfigure() { d.endpointEnable(descHIDEndpointMouse, false, descHIDConfigAttrMouse) - d.endpointConfigureTx(descHIDEndpointMouse, - hid.txMouseSize, false, nil) + // d.endpointConfigureTx(descHIDEndpointMouse, + // hid.txMouseSize, false, nil) } // ============================================================================= @@ -967,6 +1647,6 @@ func (d *dhw) joystickConfigure() { d.endpointEnable(descHIDEndpointJoystick, false, descHIDConfigAttrJoystick) - d.endpointConfigureTx(descHIDEndpointJoystick, - hid.txJoystickSize, false, nil) + // d.endpointConfigureTx(descHIDEndpointJoystick, + // hid.txJoystickSize, false, nil) } diff --git a/src/machine/usb/dhw_mimxrt1062.go b/src/machine/usb/dhw_mimxrt1062.go index feafffc18..b82c86ab6 100644 --- a/src/machine/usb/dhw_mimxrt1062.go +++ b/src/machine/usb/dhw_mimxrt1062.go @@ -1,3 +1,4 @@ +//go:build mimxrt1062 // +build mimxrt1062 package usb @@ -236,6 +237,8 @@ func (d *dhw) interrupt() { // wait for flush to complete for d.bus.ENDPTFLUSH.HasBits(0x00010001) { } + // Reset notify mask for control endpoint 0 + d.controlMask = 0 // Notify device controller driver d.event(dcdEvent{ id: dcdEventControlSetup, @@ -248,7 +251,11 @@ func (d *dhw) interrupt() { if 0 != completeStatus { d.bus.ENDPTCOMPLETE.Set(completeStatus) if 0 != completeStatus&d.controlMask { - d.controlComplete(completeStatus) + // Clear notify mask for control endpoint 0 + d.controlMask = 0 + // Notify device controller driver, which invokes any appropriate + // callback(s) for the current device class configuration. + d.controlComplete() } completeStatus &= d.endpointMask if 0 != completeStatus { @@ -289,6 +296,7 @@ func (d *dhw) interrupt() { } d.bus.ENDPTFLUSH.Set(0xFFFFFFFF) d.event(dcdEvent{id: dcdEventStatusReset}) + d.endpointMask = 0 } // General Purpose Timer Interrupt 0(GPTINT0) - R/WC @@ -375,6 +383,7 @@ func (d *dhw) setDeviceAddress(addr uint16) { d.bus.DEVICEADDR.Set(nxp.USB_DEVICEADDR_USBADRA | ((uint32(addr) << nxp.USB_DEVICEADDR_USBADR_Pos) & nxp.USB_DEVICEADDR_USBADR_Msk)) + d.event(dcdEvent{id: dcdEventDeviceAddress}) } // ============================================================================= @@ -383,8 +392,8 @@ func (d *dhw) setDeviceAddress(addr uint16) { // controlStall stalls a transfer on control endpoint 0. To stall a transfer on // any other endpoint, use method endpointStall(). -func (d *dhw) controlStall() { - d.endpointStall(0) +func (d *dhw) controlStall(stall bool) { + d.endpointStall(0, stall) } // controlReceive receives (Rx, OUT) data on control endpoint 0. @@ -554,7 +563,7 @@ func (d *dhw) endpointStatus(endpoint uint8) uint16 { } // endpointStall stalls a transfer on the given endpoint. -func (d *dhw) endpointStall(endpoint uint8) { +func (d *dhw) endpointStall(endpoint uint8, stall bool) { // RXS and TXS bits at same position in all endpoint control registers. d.endpointControlRegister(endpoint).SetBits( nxp.USB_ENDPTCTRL0_RXS | nxp.USB_ENDPTCTRL0_TXS, diff --git a/src/machine/usb/queue.go b/src/machine/usb/queue.go new file mode 100644 index 000000000..c4270732f --- /dev/null +++ b/src/machine/usb/queue.go @@ -0,0 +1,131 @@ +package usb + +import ( + "errors" + "runtime/volatile" +) + +const QueueSize = 8 + +type Queue struct { + fifo [QueueSize]uint8 + tail volatile.Register32 + head volatile.Register32 +} + +var ( + ErrReadBuffer = errors.New("cannot copy into read buffer") + ErrWriteBuffer = errors.New("cannot copy from write buffer") + ErrQueueEmpty = errors.New("data queue empty") // Read Error + ErrQueueFull = errors.New("data queue full") // Write error +) + +// Reset discards all buffered data. +//go:inline +func (q *Queue) Reset() { + for i := range q.fifo { + q.fifo[i] = 0 + } + q.tail.Set(0) + q.head.Set(0) +} + +//go:inline +func (q *Queue) Len() int { + return int(q.tail.Get() - q.head.Get()) +} + +//go:inline +func (q *Queue) Cap() int { + return cap(q.fifo) +} + +func (q *Queue) Read(data []uint8) (int, error) { + + less := uint32(len(data)) + if less == 0 { + return 0, ErrReadBuffer + } // nothing to copy into + + head := q.head.Get() + used := q.tail.Get() - head + + if used == 0 { + return 0, ErrQueueEmpty + } // empty queue + + if less > used { + less = used + } // only get from used space + + for i := uint32(0); i < less; i++ { + data[i] = q.fifo[head%QueueSize] + head++ + } + q.head.Set(head) + + return int(less), nil +} + +func (q *Queue) Write(data []uint8) (int, error) { + + more := uint32(len(data)) + if more == 0 { + return 0, ErrWriteBuffer + } // nothing to copy from + + tail := q.tail.Get() + used := tail - q.head.Get() + + if used == QueueSize { + return 0, ErrQueueFull + } // full queue + + if used+more > QueueSize { + more = QueueSize - used + } // only put to unused space + + for i := uint32(0); i < more; i++ { + q.fifo[tail%QueueSize] = data[i] + tail++ + } + q.tail.Set(tail) + + return int(more), nil +} + +func (q *Queue) Deq() (uint8, bool) { + + tail := q.head.Get() + if tail == q.tail.Get() { + return 0, false + } // empty queue + + data := q.fifo[tail%QueueSize] + q.head.Set(tail + 1) + + return data, true +} + +func (q *Queue) Front() (uint8, bool) { + + tail := q.head.Get() + if tail == q.tail.Get() { + return 0, false + } // empty queue + + return q.fifo[tail%QueueSize], true +} + +func (q *Queue) Enq(data uint8) bool { + + head := q.tail.Get() + if head-q.head.Get() == QueueSize { + return false + } // full queue + + q.fifo[head%QueueSize] = data + q.tail.Set(head + 1) + + return true +} diff --git a/src/machine/usb/uart.go b/src/machine/usb/uart.go index d50966f77..c5b776ac0 100644 --- a/src/machine/usb/uart.go +++ b/src/machine/usb/uart.go @@ -37,14 +37,18 @@ func (uart *UART) Configure(config UARTConfig) error { return nil } +func (uart *UART) Ready() bool { + return uart.core.dc.uartReady() +} + // Buffered returns the number of bytes currently stored in the RX buffer. -func (uart UART) Buffered() int { +func (uart *UART) Buffered() int { return uart.core.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) { +func (uart *UART) ReadByte() (byte, error) { n, ok := uart.core.dc.uartReadByte() if !ok { return 0, ErrUARTEmptyBuffer @@ -53,12 +57,12 @@ func (uart UART) ReadByte() (byte, error) { } // Read from the RX buffer. -func (uart UART) Read(data []byte) (n int, err error) { +func (uart *UART) Read(data []byte) (n int, err error) { return uart.core.dc.uartRead(data), nil } // WriteByte writes a single byte of data to the UART interface. -func (uart UART) WriteByte(c byte) error { +func (uart *UART) WriteByte(c byte) error { if !uart.core.dc.uartWriteByte(c) { return ErrUARTWriteFailed } @@ -66,6 +70,6 @@ func (uart UART) WriteByte(c byte) error { } // Write data to the UART. -func (uart UART) Write(data []byte) (n int, err error) { +func (uart *UART) Write(data []byte) (n int, err error) { return uart.core.dc.uartWrite(data), nil } diff --git a/src/machine/usb/util.go b/src/machine/usb/util.go index 6d8a92c03..84ece0bb2 100644 --- a/src/machine/usb/util.go +++ b/src/machine/usb/util.go @@ -216,12 +216,33 @@ func cycles(microsec, cpuFreqHz uint32) uint32 { return uint32((uint64(microsec) * uint64(cpuFreqHz)) / 1000000) } +//go:inline +func endpointValid(address uint8) bool { + return address&descEndpointInvalid == 0 +} + //go:inline func unpackEndpoint(address uint8) (number, direction uint8) { return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos, (address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos } +//go:inline +func packEndpoint(number, direction uint8) (address uint8) { + return ((number << descEndptAddrNumberPos) & descEndptAddrNumberMsk) | + ((direction << descEndptAddrDirectionPos) & descEndptAddrDirectionMsk) +} + +//go:inline +func endpointNumber(address uint8) (number uint8) { + return (address & descEndptAddrNumberMsk) >> descEndptAddrNumberPos +} + +//go:inline +func endpointDirection(address uint8) (direction uint8) { + return (address & descEndptAddrDirectionMsk) >> descEndptAddrDirectionPos +} + //go:inline func rxEndpoint(number uint8) uint8 { return (number & descEndptAddrNumberMsk) | descEndptAddrDirectionOut @@ -245,20 +266,23 @@ func indexEndpoint(index uint8) uint8 { } // wrap computes the index into a circular buffer of length mod by walking -// forward n elements if n is positive, or reverse n elements if n is negative. -// For example, both wrap(42, 10) and wrap(-308, 10) return 2. +// forward n elements if n is positive, or reverse -n elements if n is negative. +// For example, both wrap(12, 10) and wrap(-18, 10) return 2. //go:inline func wrap(n, mod int) int { - if mod <= 0 || n == mod { + if mod <= 0 { + // Buffer length (mod) must be positive. return 0 } if n < 0 { if -n < mod { + // Do not wrap around (no underflow). return mod + n } return mod - (-n % mod) } if n < mod { + // Do not wrap around (no overflow). return n } return n % mod diff --git a/src/runtime/runtime_atsamd51.go b/src/runtime/runtime_atsamd51.go index a2438bac8..7af4ba214 100644 --- a/src/runtime/runtime_atsamd51.go +++ b/src/runtime/runtime_atsamd51.go @@ -7,6 +7,7 @@ import ( "device/arm" "device/sam" "machine" + "machine/usb" "runtime/interrupt" "runtime/volatile" ) @@ -25,13 +26,12 @@ func init() { initClocks() initRTC() initSERCOMClocks() - initUSBClock() initADCClock() // connect to USB CDC interface - machine.Serial.Configure(machine.UARTConfig{}) + machine.Serial.Configure(usb.UARTConfig{}) if !machine.USB.Configured() { - machine.USB.Configure(machine.UARTConfig{}) + machine.USB.Configure(usb.UARTConfig{}) } }