From 7c9cb6d016afc3ed5b849e15d804f12c92dd3863 Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Tue, 28 Jan 2020 12:48:36 -0500 Subject: [PATCH 01/11] usb-cdc implementation for nRF52840 This is based on the implementation in machine_atsamd51.go, with changes made to use the USBD registers found on the nRF52840. --- src/machine/usb.go | 2 +- src/machine/usb_nrf52840.go | 541 ++++++++++++++++++++++++++++++++++++ 2 files changed, 542 insertions(+), 1 deletion(-) create mode 100644 src/machine/usb_nrf52840.go diff --git a/src/machine/usb.go b/src/machine/usb.go index 106693a056..747dfd3b3f 100644 --- a/src/machine/usb.go +++ b/src/machine/usb.go @@ -1,4 +1,4 @@ -// +build sam +// +build sam nrf52840 package machine diff --git a/src/machine/usb_nrf52840.go b/src/machine/usb_nrf52840.go new file mode 100644 index 0000000000..fbf3a9fcfc --- /dev/null +++ b/src/machine/usb_nrf52840.go @@ -0,0 +1,541 @@ +// +build nrf52840 +package machine + +import ( + "device/arm" + "device/nrf" + "errors" + "unsafe" +) + +// USBCDC is the USB CDC aka serial over USB interface on the nRF52840 +type USBCDC struct { + Buffer *RingBuffer +} + +// WriteByte writes a byte of data to the USB CDC interface. +func (usbcdc USBCDC) WriteByte(c byte) error { + // Supposedly to handle problem with Windows USB serial ports? + if usbLineInfo.lineState > 0 { + udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0] = c + sendViaEPIn( + usb_CDC_ENDPOINT_IN, + &udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0], + 1, + ) + + // wait for transfer to complete + timeout := 3000 + for nrf.USBD.EVENTS_ENDEPIN[usb_CDC_ENDPOINT_IN].Get() == 0 { + timeout-- + if timeout == 0 { + return errors.New("USBCDC write byte timeout") + } + } + } + + return nil +} + +func (usbcdc USBCDC) DTR() bool { + return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0 +} + +func (usbcdc USBCDC) RTS() bool { + return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0 +} + +var ( + usbEndpointDescriptors [8]usbDeviceDescriptor + + udd_ep_in_cache_buffer [7][128]uint8 + udd_ep_out_cache_buffer [7][128]uint8 + + sendOnEP0DATADONE struct { + ptr *byte + count int + } + isEndpointHalt = false + isRemoteWakeUpEnabled = false + endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL, + (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn), + (usb_ENDPOINT_TYPE_BULK | usbEndpointOut), + (usb_ENDPOINT_TYPE_BULK | usbEndpointIn)} + + usbConfiguration uint8 + usbSetInterface uint8 + usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} + epinen uint32 + epouten uint32 +) + +// Configure the USB CDC interface. The config is here for compatibility with the UART interface. +func (usbcdc USBCDC) Configure(config UARTConfig) { + // enable interrupt for end of reset and start of frame + nrf.USBD.INTEN.Set( + nrf.USBD_INTEN_SOF | + nrf.USBD_INTEN_USBRESET | + nrf.USBD_INTEN_USBEVENT | + nrf.USBD_INTEN_EP0SETUP, + ) + + // enable IRQ + arm.SetPriority(nrf.IRQ_USBD, 0xc0) + arm.EnableIRQ(nrf.IRQ_USBD) + + // enable USB + nrf.USBD.ENABLE.Set(1) + nrf.USBD.USBPULLUP.Set(0) +} + +//go:export USBD_IRQHandler +func handleUSBIRQ() { + // USBD ready event + if nrf.USBD.EVENTS_USBEVENT.Get() == 1 && + (nrf.USBD.EVENTCAUSE.Get()&nrf.USBD_EVENTCAUSE_READY) > 0 { + nrf.USBD.EVENTS_USBEVENT.Set(0) + nrf.USBD.EVENTCAUSE.Set(nrf.USBD_EVENTCAUSE_READY) + + // Configure control endpoint + initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL) + + // Enable Setup-Received interrupt + nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_EP0SETUP) + nrf.USBD.USBPULLUP.Set(1) + + usbConfiguration = 0 + } + + // Start of frame + if nrf.USBD.EVENTS_SOF.Get() == 1 { + nrf.USBD.EVENTS_SOF.Set(0) + // if you want to blink LED showing traffic, this would be the place... + } + + if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 { + // done sending packet - either need to send another or enter status stage + if sendOnEP0DATADONE.ptr != nil { + // previous data was too big for one packet, so send a second + sendViaEPIn( + 0, + sendOnEP0DATADONE.ptr, + sendOnEP0DATADONE.count, + ) + + // clear, so we know we're done + sendOnEP0DATADONE.ptr = nil + } else { + // no more data, so set status stage + nrf.USBD.TASKS_EP0STATUS.Set(1) + } + nrf.USBD.EVENTS_EP0DATADONE.Set(0) + return + } + + // Endpoint 0 Setup interrupt + if nrf.USBD.EVENTS_EP0SETUP.Get() == 1 { + // ack setup received + nrf.USBD.EVENTS_EP0SETUP.Set(0) + + // parse setup + setup := parseUSBSetupRegisters() + + ok := false + if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD { + // Standard Requests + ok = handleStandardSetup(setup) + } else { + if setup.wIndex == usb_CDC_ACM_INTERFACE { + ok = cdcSetup(setup) + } + } + + if !ok { + // Stall endpoint + nrf.USBD.TASKS_EP0STALL.Set(1) + } + } + + // Now the actual transfer handlers, ignore endpoint number 0 (setup) + if nrf.USBD.EVENTS_EPDATA.Get() > 0 { + epDataStatus := nrf.USBD.EPDATASTATUS.Get() + var i uint32 + for i = 1; i < uint32(len(endPoints)); i++ { + // Check if endpoint has a pending interrupt + inDataDone := epDataStatus&(1< 0 + outDataDone := epDataStatus&(0x10000< 0 + if inDataDone || outDataDone { + switch i { + case usb_CDC_ENDPOINT_OUT: + handleEndpoint(i) + case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM: + // TODO: do we need to do anything? + } + } + } + nrf.USBD.EVENTS_EPDATA.Set(0) + nrf.USBD.EPDATASTATUS.Set(0) + } +} + +func parseUSBSetupRegisters() usbSetup { + return usbSetup{ + bmRequestType: uint8(nrf.USBD.BMREQUESTTYPE.Get()), + bRequest: uint8(nrf.USBD.BREQUEST.Get()), + wValueL: uint8(nrf.USBD.WVALUEL.Get()), + wValueH: uint8(nrf.USBD.WVALUEH.Get()), + wIndex: uint16((nrf.USBD.WINDEXH.Get() << 8) | nrf.USBD.WINDEXL.Get()), + wLength: uint16(((nrf.USBD.WLENGTHH.Get() & 0xff) << 8) | (nrf.USBD.WLENGTHL.Get() & 0xff)), + } +} + +func initEndpoint(ep, config uint32) { + switch config { + case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn: + enableEPIn(ep) + + case usb_ENDPOINT_TYPE_BULK | usbEndpointOut: + // set data buffer address + nrf.USBD.EPOUT[ep].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) + nrf.USBD.EPOUT[ep].MAXCNT.Set(128) + nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_EPDATA) + nrf.USBD.SIZE.EPOUT[ep].Set(0) + enableEPOut(ep) + // nrf.USBD.TASKS_STARTEPOUT[ep].Set(1) + + case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut: + enableEPOut(ep) + // TODO: not really anything, seems like... + + case usb_ENDPOINT_TYPE_BULK | usbEndpointIn: + enableEPIn(ep) + + case usb_ENDPOINT_TYPE_CONTROL: + enableEPIn(0) + enableEPOut(0) + nrf.USBD.TASKS_EP0STATUS.Set(1) + } +} + +func handleStandardSetup(setup usbSetup) bool { + switch setup.bRequest { + case usb_GET_STATUS: + buf := []byte{0, 0} + + if setup.bmRequestType != 0 { // endpoint + // TODO: actually check if the endpoint in question is currently halted + if isEndpointHalt { + buf[0] = 1 + } + } + + sendUSBPacket(0, buf) + return true + + case usb_CLEAR_FEATURE: + if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP + isRemoteWakeUpEnabled = false + } else if setup.wValueL == 0 { // ENDPOINTHALT + isEndpointHalt = false + } + nrf.USBD.TASKS_EP0STATUS.Set(1) + return true + + case usb_SET_FEATURE: + if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP + isRemoteWakeUpEnabled = true + } else if setup.wValueL == 0 { // ENDPOINTHALT + isEndpointHalt = true + } + nrf.USBD.TASKS_EP0STATUS.Set(1) + return true + + case usb_SET_ADDRESS: + // nrf USBD handles this + return true + + case usb_GET_DESCRIPTOR: + sendDescriptor(setup) + return true + + case usb_SET_DESCRIPTOR: + return false + + case usb_GET_CONFIGURATION: + buff := []byte{usbConfiguration} + sendUSBPacket(0, buff) + return true + + case usb_SET_CONFIGURATION: + if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE { + for i := 1; i < len(endPoints); i++ { + initEndpoint(uint32(i), endPoints[i]) + } + + usbConfiguration = setup.wValueL + + // Enable interrupt for CDC control messages from host (OUT packet) + // setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1) + + // Enable interrupt for CDC data messages from host + // setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0) + + nrf.USBD.TASKS_EP0STATUS.Set(1) + return true + } else { + return false + } + + case usb_GET_INTERFACE: + buff := []byte{usbSetInterface} + sendUSBPacket(0, buff) + return true + + case usb_SET_INTERFACE: + usbSetInterface = setup.wValueL + + nrf.USBD.TASKS_EP0STATUS.Set(1) + return true + + default: + return true + } +} + +func cdcSetup(setup usbSetup) bool { + if setup.bmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE { + if setup.bRequest == usb_CDC_GET_LINE_CODING { + b := make([]byte, 7) + b[0] = byte(usbLineInfo.dwDTERate) + b[1] = byte(usbLineInfo.dwDTERate >> 8) + b[2] = byte(usbLineInfo.dwDTERate >> 16) + b[3] = byte(usbLineInfo.dwDTERate >> 24) + b[4] = byte(usbLineInfo.bCharFormat) + b[5] = byte(usbLineInfo.bParityType) + b[6] = byte(usbLineInfo.bDataBits) + + sendUSBPacket(0, b) + return true + } + } + + if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { + if setup.bRequest == usb_CDC_SET_LINE_CODING { + b := receiveUSBControlPacket() + usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + usbLineInfo.bCharFormat = b[4] + usbLineInfo.bParityType = b[5] + usbLineInfo.bDataBits = b[6] + } + + if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE { + usbLineInfo.lineState = setup.wValueL + } + + if setup.bRequest == usb_CDC_SET_LINE_CODING || setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE { + // auto-reset into the bootloader + if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 { + // TODO: do we want to do this on the nRF52840? + // ResetProcessor() + } else { + // TODO: cancel any reset + } + nrf.USBD.TASKS_EP0STATUS.Set(1) + } + + if setup.bRequest == usb_CDC_SEND_BREAK { + // TODO: something with this value? + // breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL; + // return false; + nrf.USBD.TASKS_EP0STATUS.Set(1) + } + return true + } + return false +} + +func sendUSBPacket(ep uint32, data []byte) { + count := len(data) + copy(udd_ep_in_cache_buffer[ep][:], data) + if ep == 0 && count > usbEndpointPacketSize { + sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[ep][usbEndpointPacketSize] + sendOnEP0DATADONE.count = count - usbEndpointPacketSize + count = usbEndpointPacketSize + } + sendViaEPIn( + ep, + &udd_ep_in_cache_buffer[ep][0], + count, + ) +} + +func receiveUSBControlPacket() []byte { + // address + nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0][0])))) + nrf.USBD.EPOUT[0].MAXCNT.Set(64) + + // set byte count to zero + nrf.USBD.SIZE.EPOUT[0].Set(0) + nrf.USBD.SHORTS.Set(nrf.USBD_SHORTS_ENDEPOUT0_EP0RCVOUT) + nrf.USBD.TASKS_STARTEPOUT[0].Set(1) + + // Wait until OUT transfer is ready. + timeout := 300000 + for nrf.USBD.EVENTS_EP0DATADONE.Get() == 0 { + timeout-- + if timeout == 0 { + return []byte{} + } + } + + // get data + bytesread := int(nrf.USBD.SIZE.EPOUT[0].Get()) + + data := make([]byte, bytesread) + copy(data, udd_ep_out_cache_buffer[0][:]) + + return data +} + +// sendDescriptor creates and sends the various USB descriptor types that +// can be requested by the host. +func sendDescriptor(setup usbSetup) { + switch setup.wValueH { + case usb_CONFIGURATION_DESCRIPTOR_TYPE: + sendConfiguration(setup) + return + case usb_DEVICE_DESCRIPTOR_TYPE: + if setup.wLength == 8 { + // composite descriptor requested, so only send 8 bytes + dd := NewDeviceDescriptor(0xEF, 0x02, 0x01, 64, usb_VID, usb_PID, 0x100, usb_IMANUFACTURER, usb_IPRODUCT, usb_ISERIAL, 1) + sendUSBPacket(0, dd.Bytes()[:8]) + } else { + // complete descriptor requested so send entire packet + dd := NewDeviceDescriptor(0x00, 0x00, 0x00, 64, usb_VID, usb_PID, 0x100, usb_IMANUFACTURER, usb_IPRODUCT, usb_ISERIAL, 1) + sendUSBPacket(0, dd.Bytes()) + } + return + + case usb_STRING_DESCRIPTOR_TYPE: + switch setup.wValueL { + case 0: + b := make([]byte, 4) + b[0] = byte(usb_STRING_LANGUAGE[0] >> 8) + b[1] = byte(usb_STRING_LANGUAGE[0] & 0xff) + b[2] = byte(usb_STRING_LANGUAGE[1] >> 8) + b[3] = byte(usb_STRING_LANGUAGE[1] & 0xff) + sendUSBPacket(0, b) + + case usb_IPRODUCT: + b := strToUTF16Descriptor(usb_STRING_PRODUCT) + sendUSBPacket(0, b[:setup.wLength]) + + case usb_IMANUFACTURER: + b := strToUTF16Descriptor(usb_STRING_MANUFACTURER) + sendUSBPacket(0, b[:setup.wLength]) + + case usb_ISERIAL: + // TODO: allow returning a product serial number + nrf.USBD.TASKS_EP0STATUS.Set(1) + } + return + } + + // do not know how to handle this message, so return zero + nrf.USBD.TASKS_EP0STATUS.Set(1) + return +} + +// sendConfiguration creates and sends the configuration packet to the host. +func sendConfiguration(setup usbSetup) { + if setup.wLength == 9 { + sz := uint16(configDescriptorSize + cdcSize) + config := NewConfigDescriptor(sz, 2) + + sendUSBPacket(0, config.Bytes()) + } else { + iad := NewIADDescriptor(0, 2, usb_CDC_COMMUNICATION_INTERFACE_CLASS, usb_CDC_ABSTRACT_CONTROL_MODEL, 0) + + cif := NewInterfaceDescriptor(usb_CDC_ACM_INTERFACE, 1, usb_CDC_COMMUNICATION_INTERFACE_CLASS, usb_CDC_ABSTRACT_CONTROL_MODEL, 0) + + header := NewCDCCSInterfaceDescriptor(usb_CDC_HEADER, usb_CDC_V1_10&0xFF, (usb_CDC_V1_10>>8)&0x0FF) + + controlManagement := NewACMFunctionalDescriptor(usb_CDC_ABSTRACT_CONTROL_MANAGEMENT, 6) + + functionalDescriptor := NewCDCCSInterfaceDescriptor(usb_CDC_UNION, usb_CDC_ACM_INTERFACE, usb_CDC_DATA_INTERFACE) + + callManagement := NewCMFunctionalDescriptor(usb_CDC_CALL_MANAGEMENT, 1, 1) + + cifin := NewEndpointDescriptor((usb_CDC_ENDPOINT_ACM | usbEndpointIn), usb_ENDPOINT_TYPE_INTERRUPT, 0x10, 0x10) + + dif := NewInterfaceDescriptor(usb_CDC_DATA_INTERFACE, 2, usb_CDC_DATA_INTERFACE_CLASS, 0, 0) + + out := NewEndpointDescriptor((usb_CDC_ENDPOINT_OUT | usbEndpointOut), usb_ENDPOINT_TYPE_BULK, usbEndpointPacketSize, 0) + + in := NewEndpointDescriptor((usb_CDC_ENDPOINT_IN | usbEndpointIn), usb_ENDPOINT_TYPE_BULK, usbEndpointPacketSize, 0) + + cdc := NewCDCDescriptor(iad, + cif, + header, + controlManagement, + functionalDescriptor, + callManagement, + cifin, + dif, + out, + in) + + sz := uint16(configDescriptorSize + cdcSize) + config := NewConfigDescriptor(sz, 2) + + buf := make([]byte, 0) + buf = append(buf, config.Bytes()...) + buf = append(buf, cdc.Bytes()...) + sendUSBPacket(0, buf) + } +} + +func handleEndpoint(ep uint32) { + // get data + count := int(nrf.USBD.SIZE.EPOUT[ep].Get()) + // move to ring buffer + for i := 0; i < count; i++ { + UART0.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF))) + } + + // set ready for next data + nrf.USBD.SIZE.EPOUT[ep].Set(0) + nrf.USBD.TASKS_STARTEPOUT[ep].Set(1) +} + +func sendViaEPIn(ep uint32, ptr *byte, count int) { + nrf.USBD.EPIN[ep].PTR.Set( + uint32(uintptr(unsafe.Pointer(ptr))), + ) + nrf.USBD.EPIN[ep].MAXCNT.Set(uint32(count)) + nrf.USBD.EVENTS_ENDEPIN[ep].Set(0) + nrf.USBD.TASKS_STARTEPIN[ep].Set(1) +} + +func enableEPOut(ep uint32) { + epouten = epouten | (nrf.USBD_EPOUTEN_OUT0 << ep) + nrf.USBD.EPOUTEN.Set(epouten) +} + +func enableEPIn(ep uint32) { + epinen = epinen | (nrf.USBD_EPINEN_IN0 << ep) + nrf.USBD.EPINEN.Set(epinen) +} + +func strToUTF16Descriptor(in string) []byte { + size := (len(in)<<1) + 2 + out := make([]byte, size) + out[0] = byte(size) + out[1] = 0x03 + // TODO: do a "real" conversion that supports more than just ascii chars + for i, char := range in { + out[i<<1 + 2] = 0 + out[i<<1 + 3] = byte(char) + } + return out +} From 8b281ae15b0e525651802d480388805fe0c0b68e Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Tue, 28 Jan 2020 22:01:05 -0500 Subject: [PATCH 02/11] use new interrupt framework; convert to utf16le descriptors --- src/machine/usb_nrf52840.go | 40 ++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/machine/usb_nrf52840.go b/src/machine/usb_nrf52840.go index fbf3a9fcfc..3e4b4a8a5f 100644 --- a/src/machine/usb_nrf52840.go +++ b/src/machine/usb_nrf52840.go @@ -1,10 +1,12 @@ // +build nrf52840 + package machine import ( - "device/arm" "device/nrf" "errors" + "runtime/interrupt" + "unicode/utf16" "unsafe" ) @@ -46,6 +48,8 @@ func (usbcdc USBCDC) RTS() bool { } var ( + usbdInterrupt = interrupt.New(nrf.IRQ_USBD, handleUSBIRQ) + usbEndpointDescriptors [8]usbDeviceDescriptor udd_ep_in_cache_buffer [7][128]uint8 @@ -80,16 +84,15 @@ func (usbcdc USBCDC) Configure(config UARTConfig) { ) // enable IRQ - arm.SetPriority(nrf.IRQ_USBD, 0xc0) - arm.EnableIRQ(nrf.IRQ_USBD) + usbdInterrupt.SetPriority(0xe0) + usbdInterrupt.Enable() // enable USB nrf.USBD.ENABLE.Set(1) nrf.USBD.USBPULLUP.Set(0) } -//go:export USBD_IRQHandler -func handleUSBIRQ() { +func handleUSBIRQ(interrupt.Interrupt) { // USBD ready event if nrf.USBD.EVENTS_USBEVENT.Get() == 1 && (nrf.USBD.EVENTCAUSE.Get()&nrf.USBD_EVENTCAUSE_READY) > 0 { @@ -181,11 +184,11 @@ func handleUSBIRQ() { func parseUSBSetupRegisters() usbSetup { return usbSetup{ bmRequestType: uint8(nrf.USBD.BMREQUESTTYPE.Get()), - bRequest: uint8(nrf.USBD.BREQUEST.Get()), - wValueL: uint8(nrf.USBD.WVALUEL.Get()), - wValueH: uint8(nrf.USBD.WVALUEH.Get()), - wIndex: uint16((nrf.USBD.WINDEXH.Get() << 8) | nrf.USBD.WINDEXL.Get()), - wLength: uint16(((nrf.USBD.WLENGTHH.Get() & 0xff) << 8) | (nrf.USBD.WLENGTHL.Get() & 0xff)), + bRequest: uint8(nrf.USBD.BREQUEST.Get()), + wValueL: uint8(nrf.USBD.WVALUEL.Get()), + wValueH: uint8(nrf.USBD.WVALUEH.Get()), + wIndex: uint16((nrf.USBD.WINDEXH.Get() << 8) | nrf.USBD.WINDEXL.Get()), + wLength: uint16(((nrf.USBD.WLENGTHH.Get() & 0xff) << 8) | (nrf.USBD.WLENGTHL.Get() & 0xff)), } } @@ -427,11 +430,11 @@ func sendDescriptor(setup usbSetup) { sendUSBPacket(0, b) case usb_IPRODUCT: - b := strToUTF16Descriptor(usb_STRING_PRODUCT) + b := strToUTF16LEDescriptor(usb_STRING_PRODUCT) sendUSBPacket(0, b[:setup.wLength]) case usb_IMANUFACTURER: - b := strToUTF16Descriptor(usb_STRING_MANUFACTURER) + b := strToUTF16LEDescriptor(usb_STRING_MANUFACTURER) sendUSBPacket(0, b[:setup.wLength]) case usb_ISERIAL: @@ -527,15 +530,16 @@ func enableEPIn(ep uint32) { nrf.USBD.EPINEN.Set(epinen) } -func strToUTF16Descriptor(in string) []byte { - size := (len(in)<<1) + 2 +func strToUTF16LEDescriptor(in string) []byte { + runes := []rune(in) + encoded := utf16.Encode(runes) + size := (len(encoded) << 1) + 2 out := make([]byte, size) out[0] = byte(size) out[1] = 0x03 - // TODO: do a "real" conversion that supports more than just ascii chars - for i, char := range in { - out[i<<1 + 2] = 0 - out[i<<1 + 3] = byte(char) + for i, value := range encoded { + out[(i<<1)+2] = byte(value & 0xff) + out[(i<<1)+3] = byte(value >> 8) } return out } From 4b12633468e269875e6da56f341905c222d36384 Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Wed, 29 Jan 2020 15:36:53 -0500 Subject: [PATCH 03/11] move interrupt to a member of the USBCDC struct --- src/machine/usb_nrf52840.go | 47 +++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/src/machine/usb_nrf52840.go b/src/machine/usb_nrf52840.go index 3e4b4a8a5f..68624bd8d3 100644 --- a/src/machine/usb_nrf52840.go +++ b/src/machine/usb_nrf52840.go @@ -12,7 +12,8 @@ import ( // USBCDC is the USB CDC aka serial over USB interface on the nRF52840 type USBCDC struct { - Buffer *RingBuffer + Buffer *RingBuffer + interrupt interrupt.Interrupt } // WriteByte writes a byte of data to the USB CDC interface. @@ -27,7 +28,7 @@ func (usbcdc USBCDC) WriteByte(c byte) error { ) // wait for transfer to complete - timeout := 3000 + timeout := 30000 for nrf.USBD.EVENTS_ENDEPIN[usb_CDC_ENDPOINT_IN].Get() == 0 { timeout-- if timeout == 0 { @@ -48,8 +49,7 @@ func (usbcdc USBCDC) RTS() bool { } var ( - usbdInterrupt = interrupt.New(nrf.IRQ_USBD, handleUSBIRQ) - + USB = USBCDC{Buffer: NewRingBuffer()} usbEndpointDescriptors [8]usbDeviceDescriptor udd_ep_in_cache_buffer [7][128]uint8 @@ -74,25 +74,27 @@ var ( ) // Configure the USB CDC interface. The config is here for compatibility with the UART interface. -func (usbcdc USBCDC) Configure(config UARTConfig) { - // enable interrupt for end of reset and start of frame - nrf.USBD.INTEN.Set( - nrf.USBD_INTEN_SOF | - nrf.USBD_INTEN_USBRESET | - nrf.USBD_INTEN_USBEVENT | - nrf.USBD_INTEN_EP0SETUP, - ) - +func (usbcdc *USBCDC) Configure(config UARTConfig) { // enable IRQ - usbdInterrupt.SetPriority(0xe0) - usbdInterrupt.Enable() + usbcdc.interrupt = interrupt.New(nrf.IRQ_USBD, USB.handleInterrupt) + usbcdc.interrupt.SetPriority(0xDF) + usbcdc.interrupt.Enable() // enable USB nrf.USBD.ENABLE.Set(1) + + // enable interrupt for end of reset and start of frame + nrf.USBD.INTENSET.Set( + nrf.USBD_INTENSET_SOF | + nrf.USBD_INTENSET_EP0DATADONE | + nrf.USBD_INTENSET_USBEVENT | + nrf.USBD_INTENSET_EP0SETUP, + ) + nrf.USBD.USBPULLUP.Set(0) } -func handleUSBIRQ(interrupt.Interrupt) { +func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { // USBD ready event if nrf.USBD.EVENTS_USBEVENT.Get() == 1 && (nrf.USBD.EVENTCAUSE.Get()&nrf.USBD_EVENTCAUSE_READY) > 0 { @@ -172,7 +174,7 @@ func handleUSBIRQ(interrupt.Interrupt) { case usb_CDC_ENDPOINT_OUT: handleEndpoint(i) case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM: - // TODO: do we need to do anything? + // nothing to do here } } } @@ -200,7 +202,7 @@ func initEndpoint(ep, config uint32) { case usb_ENDPOINT_TYPE_BULK | usbEndpointOut: // set data buffer address nrf.USBD.EPOUT[ep].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) - nrf.USBD.EPOUT[ep].MAXCNT.Set(128) + nrf.USBD.EPOUT[ep].MAXCNT.Set(64) nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_EPDATA) nrf.USBD.SIZE.EPOUT[ep].Set(0) enableEPOut(ep) @@ -276,13 +278,6 @@ func handleStandardSetup(setup usbSetup) bool { } usbConfiguration = setup.wValueL - - // Enable interrupt for CDC control messages from host (OUT packet) - // setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1) - - // Enable interrupt for CDC data messages from host - // setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0) - nrf.USBD.TASKS_EP0STATUS.Set(1) return true } else { @@ -503,7 +498,7 @@ func handleEndpoint(ep uint32) { count := int(nrf.USBD.SIZE.EPOUT[ep].Get()) // move to ring buffer for i := 0; i < count; i++ { - UART0.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF))) + USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF))) } // set ready for next data From c026ffa5dabe434731a76aa575c844d61a2af6d6 Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Thu, 30 Jan 2020 23:04:00 -0500 Subject: [PATCH 04/11] properly reset the USBEVENT interrupt event --- src/machine/usb_nrf52840.go | 39 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/machine/usb_nrf52840.go b/src/machine/usb_nrf52840.go index 68624bd8d3..a1bf9907b3 100644 --- a/src/machine/usb_nrf52840.go +++ b/src/machine/usb_nrf52840.go @@ -4,10 +4,11 @@ package machine import ( "device/nrf" - "errors" + "device/arm" "runtime/interrupt" "unicode/utf16" "unsafe" + "runtime/volatile" ) // USBCDC is the USB CDC aka serial over USB interface on the nRF52840 @@ -20,6 +21,7 @@ type USBCDC struct { func (usbcdc USBCDC) WriteByte(c byte) error { // Supposedly to handle problem with Windows USB serial ports? if usbLineInfo.lineState > 0 { + cdcInBusy.Set(1) udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0] = c sendViaEPIn( usb_CDC_ENDPOINT_IN, @@ -27,13 +29,8 @@ func (usbcdc USBCDC) WriteByte(c byte) error { 1, ) - // wait for transfer to complete - timeout := 30000 - for nrf.USBD.EVENTS_ENDEPIN[usb_CDC_ENDPOINT_IN].Get() == 0 { - timeout-- - if timeout == 0 { - return errors.New("USBCDC write byte timeout") - } + for cdcInBusy.Get() == 1 { + arm.Asm("wfi") } } @@ -71,6 +68,7 @@ var ( usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} epinen uint32 epouten uint32 + cdcInBusy volatile.Register8 ) // Configure the USB CDC interface. The config is here for compatibility with the UART interface. @@ -96,19 +94,20 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) { func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { // USBD ready event - if nrf.USBD.EVENTS_USBEVENT.Get() == 1 && - (nrf.USBD.EVENTCAUSE.Get()&nrf.USBD_EVENTCAUSE_READY) > 0 { - nrf.USBD.EVENTS_USBEVENT.Set(0) - nrf.USBD.EVENTCAUSE.Set(nrf.USBD_EVENTCAUSE_READY) + if nrf.USBD.EVENTS_USBEVENT.Get() == 1 { + if (nrf.USBD.EVENTCAUSE.Get()&nrf.USBD_EVENTCAUSE_READY) > 0 { - // Configure control endpoint - initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL) + // Configure control endpoint + initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL) - // Enable Setup-Received interrupt - nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_EP0SETUP) - nrf.USBD.USBPULLUP.Set(1) + // Enable Setup-Received interrupt + nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_EP0SETUP) + nrf.USBD.USBPULLUP.Set(1) - usbConfiguration = 0 + usbConfiguration = 0 + } + nrf.USBD.EVENTS_USBEVENT.Set(0) + nrf.USBD.EVENTCAUSE.Set(0) } // Start of frame @@ -174,7 +173,7 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { case usb_CDC_ENDPOINT_OUT: handleEndpoint(i) case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM: - // nothing to do here + cdcInBusy.Set(0) } } } @@ -503,6 +502,8 @@ func handleEndpoint(ep uint32) { // set ready for next data nrf.USBD.SIZE.EPOUT[ep].Set(0) + nrf.USBD.EPOUT[ep].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep][0])))) + nrf.USBD.EPOUT[ep].MAXCNT.Set(64) nrf.USBD.TASKS_STARTEPOUT[ep].Set(1) } From 0f8c456838fd730bc3792fb136b97ffcd414d87a Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Sun, 2 Feb 2020 22:52:49 -0500 Subject: [PATCH 05/11] fix reading data coming from host It turns out I needed to read the manual a little better in order to see the sequence of actions and events that the nrf USBD device expects. --- src/machine/usb_nrf52840.go | 68 ++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/src/machine/usb_nrf52840.go b/src/machine/usb_nrf52840.go index a1bf9907b3..fdcd624a11 100644 --- a/src/machine/usb_nrf52840.go +++ b/src/machine/usb_nrf52840.go @@ -3,12 +3,12 @@ package machine import ( - "device/nrf" "device/arm" + "device/nrf" "runtime/interrupt" + "runtime/volatile" "unicode/utf16" "unsafe" - "runtime/volatile" ) // USBCDC is the USB CDC aka serial over USB interface on the nRF52840 @@ -68,7 +68,7 @@ var ( usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} epinen uint32 epouten uint32 - cdcInBusy volatile.Register8 + cdcInBusy volatile.Register8 ) // Configure the USB CDC interface. The config is here for compatibility with the UART interface. @@ -95,7 +95,8 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) { func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { // USBD ready event if nrf.USBD.EVENTS_USBEVENT.Get() == 1 { - if (nrf.USBD.EVENTCAUSE.Get()&nrf.USBD_EVENTCAUSE_READY) > 0 { + nrf.USBD.EVENTS_USBEVENT.Set(0) + if (nrf.USBD.EVENTCAUSE.Get() & nrf.USBD_EVENTCAUSE_READY) > 0 { // Configure control endpoint initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL) @@ -106,7 +107,6 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { usbConfiguration = 0 } - nrf.USBD.EVENTS_USBEVENT.Set(0) nrf.USBD.EVENTCAUSE.Set(0) } @@ -118,6 +118,7 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 { // done sending packet - either need to send another or enter status stage + nrf.USBD.EVENTS_EP0DATADONE.Set(0) if sendOnEP0DATADONE.ptr != nil { // previous data was too big for one packet, so send a second sendViaEPIn( @@ -132,7 +133,6 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { // no more data, so set status stage nrf.USBD.TASKS_EP0STATUS.Set(1) } - nrf.USBD.EVENTS_EP0DATADONE.Set(0) return } @@ -162,7 +162,9 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { // Now the actual transfer handlers, ignore endpoint number 0 (setup) if nrf.USBD.EVENTS_EPDATA.Get() > 0 { + nrf.USBD.EVENTS_EPDATA.Set(0) epDataStatus := nrf.USBD.EPDATASTATUS.Get() + nrf.USBD.EPDATASTATUS.Set(0) var i uint32 for i = 1; i < uint32(len(endPoints)); i++ { // Check if endpoint has a pending interrupt @@ -171,14 +173,21 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { if inDataDone || outDataDone { switch i { case usb_CDC_ENDPOINT_OUT: - handleEndpoint(i) + nrf.USBD.EPOUT[i].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[i])))) + nrf.USBD.EPOUT[i].MAXCNT.Set(64) + nrf.USBD.TASKS_STARTEPOUT[i].Set(1) case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM: cdcInBusy.Set(0) } } } - nrf.USBD.EVENTS_EPDATA.Set(0) - nrf.USBD.EPDATASTATUS.Set(0) + } + + for i := 0; i < len(endPoints); i++ { + if nrf.USBD.EVENTS_ENDEPOUT[i].Get() > 0 { + nrf.USBD.EVENTS_ENDEPOUT[i].Set(0) + handleEndpoint(uint32(i)) + } } } @@ -199,24 +208,26 @@ func initEndpoint(ep, config uint32) { enableEPIn(ep) case usb_ENDPOINT_TYPE_BULK | usbEndpointOut: - // set data buffer address - nrf.USBD.EPOUT[ep].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) - nrf.USBD.EPOUT[ep].MAXCNT.Set(64) nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_EPDATA) + nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0 << ep) nrf.USBD.SIZE.EPOUT[ep].Set(0) enableEPOut(ep) - // nrf.USBD.TASKS_STARTEPOUT[ep].Set(1) case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut: + nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0 << ep) + nrf.USBD.SIZE.EPOUT[ep].Set(0) enableEPOut(ep) - // TODO: not really anything, seems like... case usb_ENDPOINT_TYPE_BULK | usbEndpointIn: + nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_EPDATA) enableEPIn(ep) case usb_ENDPOINT_TYPE_CONTROL: enableEPIn(0) enableEPOut(0) + nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0])))) + nrf.USBD.EPOUT[0].MAXCNT.Set(64) + nrf.USBD.TASKS_STARTEPOUT[0].Set(1) nrf.USBD.TASKS_EP0STATUS.Set(1) } } @@ -272,12 +283,12 @@ func handleStandardSetup(setup usbSetup) bool { case usb_SET_CONFIGURATION: if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE { + nrf.USBD.TASKS_EP0STATUS.Set(1) for i := 1; i < len(endPoints); i++ { initEndpoint(uint32(i), endPoints[i]) } usbConfiguration = setup.wValueL - nrf.USBD.TASKS_EP0STATUS.Set(1) return true } else { return false @@ -319,10 +330,12 @@ func cdcSetup(setup usbSetup) bool { if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { if setup.bRequest == usb_CDC_SET_LINE_CODING { b := receiveUSBControlPacket() - usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 - usbLineInfo.bCharFormat = b[4] - usbLineInfo.bParityType = b[5] - usbLineInfo.bDataBits = b[6] + if len(b) >= 7 { + usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + usbLineInfo.bCharFormat = b[4] + usbLineInfo.bParityType = b[5] + usbLineInfo.bDataBits = b[6] + } } if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE { @@ -367,17 +380,12 @@ func sendUSBPacket(ep uint32, data []byte) { } func receiveUSBControlPacket() []byte { - // address - nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0][0])))) - nrf.USBD.EPOUT[0].MAXCNT.Set(64) - // set byte count to zero nrf.USBD.SIZE.EPOUT[0].Set(0) - nrf.USBD.SHORTS.Set(nrf.USBD_SHORTS_ENDEPOUT0_EP0RCVOUT) - nrf.USBD.TASKS_STARTEPOUT[0].Set(1) + nrf.USBD.TASKS_EP0RCVOUT.Set(1) // Wait until OUT transfer is ready. - timeout := 300000 + timeout := 3000 for nrf.USBD.EVENTS_EP0DATADONE.Get() == 0 { timeout-- if timeout == 0 { @@ -387,10 +395,8 @@ func receiveUSBControlPacket() []byte { // get data bytesread := int(nrf.USBD.SIZE.EPOUT[0].Get()) - data := make([]byte, bytesread) copy(data, udd_ep_out_cache_buffer[0][:]) - return data } @@ -495,16 +501,14 @@ func sendConfiguration(setup usbSetup) { func handleEndpoint(ep uint32) { // get data count := int(nrf.USBD.SIZE.EPOUT[ep].Get()) + // 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])) } // set ready for next data nrf.USBD.SIZE.EPOUT[ep].Set(0) - nrf.USBD.EPOUT[ep].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep][0])))) - nrf.USBD.EPOUT[ep].MAXCNT.Set(64) - nrf.USBD.TASKS_STARTEPOUT[ep].Set(1) } func sendViaEPIn(ep uint32, ptr *byte, count int) { From 1cbec3839a60cc2bfbef21997e39fbbab8b890bf Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Tue, 4 Feb 2020 21:31:56 -0600 Subject: [PATCH 06/11] remove unicode/utf16 pkg, properly reset EPDATASTATUS register Previously it was attempting to reset the register by setting it to zero, but the manual says to reset bits you need to set them to 1. Once this was changed, the device began properly acknowledging when it received data from the host on the out endpoint. --- src/machine/usb.go | 17 ++++++ src/machine/usb_nrf52840.go | 101 +++++++++++++++++++----------------- 2 files changed, 70 insertions(+), 48 deletions(-) diff --git a/src/machine/usb.go b/src/machine/usb.go index 747dfd3b3f..345b55f59f 100644 --- a/src/machine/usb.go +++ b/src/machine/usb.go @@ -376,6 +376,23 @@ type cdcLineInfo struct { lineState uint8 } +// strToUTF16LEDescriptor converts a utf8 string into a string descriptor +// note: the following code only converts ascii characters to UTF16LE. In order +// to do a "proper" conversion, we would need to pull in the 'unicode/utf16' +// package, which at the time this was written added 512 bytes to the compiled +// binary. +func strToUTF16LEDescriptor(in string) []byte { + size := (len(in) << 1) + 2 + out := make([]byte, size) + out[0] = byte(size) + out[1] = 0x03 + for i, rune := range in { + out[(i<<1)+2] = byte(rune) + out[(i<<1)+3] = 0 + } + return out +} + var ( // TODO: allow setting these usb_STRING_LANGUAGE = [2]uint16{(3 << 8) | (2 + 2), 0x0409} // English diff --git a/src/machine/usb_nrf52840.go b/src/machine/usb_nrf52840.go index fdcd624a11..5fb1867ff8 100644 --- a/src/machine/usb_nrf52840.go +++ b/src/machine/usb_nrf52840.go @@ -7,7 +7,6 @@ import ( "device/nrf" "runtime/interrupt" "runtime/volatile" - "unicode/utf16" "unsafe" ) @@ -21,17 +20,13 @@ type USBCDC struct { func (usbcdc USBCDC) WriteByte(c byte) error { // Supposedly to handle problem with Windows USB serial ports? if usbLineInfo.lineState > 0 { - cdcInBusy.Set(1) + enterCriticalSection() udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0] = c sendViaEPIn( usb_CDC_ENDPOINT_IN, &udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0], 1, ) - - for cdcInBusy.Get() == 1 { - arm.Asm("wfi") - } } return nil @@ -46,7 +41,8 @@ func (usbcdc USBCDC) RTS() bool { } var ( - USB = USBCDC{Buffer: NewRingBuffer()} + USB = USBCDC{Buffer: NewRingBuffer()} + usbEndpointDescriptors [8]usbDeviceDescriptor udd_ep_in_cache_buffer [7][128]uint8 @@ -68,9 +64,26 @@ var ( usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} epinen uint32 epouten uint32 - cdcInBusy volatile.Register8 + easyDMABusy volatile.Register8 ) +// enterCriticalSection is used to protect access to easyDMA - only one thing +// can be done with it at a time +func enterCriticalSection() { + waitForEasyDMA() + easyDMABusy.SetBits(1) +} + +func waitForEasyDMA() { + for easyDMABusy.HasBits(1) { + arm.Asm("wfi") + } +} + +func exitCriticalSection() { + easyDMABusy.ClearBits(1) +} + // Configure the USB CDC interface. The config is here for compatibility with the UART interface. func (usbcdc *USBCDC) Configure(config UARTConfig) { // enable IRQ @@ -83,7 +96,7 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) { // enable interrupt for end of reset and start of frame nrf.USBD.INTENSET.Set( - nrf.USBD_INTENSET_SOF | + nrf.USBD_INTENSET_EPDATA | nrf.USBD_INTENSET_EP0DATADONE | nrf.USBD_INTENSET_USBEVENT | nrf.USBD_INTENSET_EP0SETUP, @@ -110,12 +123,6 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { nrf.USBD.EVENTCAUSE.Set(0) } - // Start of frame - if nrf.USBD.EVENTS_SOF.Get() == 1 { - nrf.USBD.EVENTS_SOF.Set(0) - // if you want to blink LED showing traffic, this would be the place... - } - if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 { // done sending packet - either need to send another or enter status stage nrf.USBD.EVENTS_EP0DATADONE.Set(0) @@ -164,29 +171,40 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { if nrf.USBD.EVENTS_EPDATA.Get() > 0 { nrf.USBD.EVENTS_EPDATA.Set(0) epDataStatus := nrf.USBD.EPDATASTATUS.Get() - nrf.USBD.EPDATASTATUS.Set(0) + nrf.USBD.EPDATASTATUS.Set(epDataStatus) var i uint32 for i = 1; i < uint32(len(endPoints)); i++ { // Check if endpoint has a pending interrupt - inDataDone := epDataStatus&(1< 0 - outDataDone := epDataStatus&(0x10000< 0 + inDataDone := epDataStatus&(nrf.USBD_EPDATASTATUS_EPIN1<<(i-1)) > 0 + outDataDone := epDataStatus&(nrf.USBD_EPDATASTATUS_EPOUT1<<(i-1)) > 0 if inDataDone || outDataDone { switch i { case usb_CDC_ENDPOINT_OUT: - nrf.USBD.EPOUT[i].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[i])))) - nrf.USBD.EPOUT[i].MAXCNT.Set(64) - nrf.USBD.TASKS_STARTEPOUT[i].Set(1) - case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM: - cdcInBusy.Set(0) + // setup buffer to receive from host + if outDataDone { + enterCriticalSection() + nrf.USBD.EPOUT[i].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[i])))) + count := nrf.USBD.SIZE.EPOUT[i].Get() + nrf.USBD.EPOUT[i].MAXCNT.Set(count) + nrf.USBD.TASKS_STARTEPOUT[i].Set(1) + } + case usb_CDC_ENDPOINT_IN: //, usb_CDC_ENDPOINT_ACM: + if inDataDone { + exitCriticalSection() + } } } } } + // ENDEPOUT[n] events for i := 0; i < len(endPoints); i++ { if nrf.USBD.EVENTS_ENDEPOUT[i].Get() > 0 { nrf.USBD.EVENTS_ENDEPOUT[i].Set(0) - handleEndpoint(uint32(i)) + if i == usb_CDC_ENDPOINT_OUT { + handleEndpoint(uint32(i)) + } + exitCriticalSection() } } } @@ -208,7 +226,6 @@ func initEndpoint(ep, config uint32) { enableEPIn(ep) case usb_ENDPOINT_TYPE_BULK | usbEndpointOut: - nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_EPDATA) nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0 << ep) nrf.USBD.SIZE.EPOUT[ep].Set(0) enableEPOut(ep) @@ -219,7 +236,6 @@ func initEndpoint(ep, config uint32) { enableEPOut(ep) case usb_ENDPOINT_TYPE_BULK | usbEndpointIn: - nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_EPDATA) enableEPIn(ep) case usb_ENDPOINT_TYPE_CONTROL: @@ -238,7 +254,6 @@ func handleStandardSetup(setup usbSetup) bool { buf := []byte{0, 0} if setup.bmRequestType != 0 { // endpoint - // TODO: actually check if the endpoint in question is currently halted if isEndpointHalt { buf[0] = 1 } @@ -354,9 +369,6 @@ func cdcSetup(setup usbSetup) bool { } if setup.bRequest == usb_CDC_SEND_BREAK { - // TODO: something with this value? - // breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL; - // return false; nrf.USBD.TASKS_EP0STATUS.Set(1) } return true @@ -431,11 +443,19 @@ func sendDescriptor(setup usbSetup) { case usb_IPRODUCT: b := strToUTF16LEDescriptor(usb_STRING_PRODUCT) - sendUSBPacket(0, b[:setup.wLength]) + if setup.wLength == 2 { + sendUSBPacket(0, b[:2]) + } else { + sendUSBPacket(0, b) + } case usb_IMANUFACTURER: b := strToUTF16LEDescriptor(usb_STRING_MANUFACTURER) - sendUSBPacket(0, b[:setup.wLength]) + if setup.wLength == 2 { + sendUSBPacket(0, b[:2]) + } else { + sendUSBPacket(0, b) + } case usb_ISERIAL: // TODO: allow returning a product serial number @@ -500,7 +520,7 @@ func sendConfiguration(setup usbSetup) { func handleEndpoint(ep uint32) { // get data - count := int(nrf.USBD.SIZE.EPOUT[ep].Get()) + count := int(nrf.USBD.EPOUT[ep].AMOUNT.Get()) // move to ring buffer for i := 0; i < count; i++ { @@ -516,7 +536,6 @@ func sendViaEPIn(ep uint32, ptr *byte, count int) { uint32(uintptr(unsafe.Pointer(ptr))), ) nrf.USBD.EPIN[ep].MAXCNT.Set(uint32(count)) - nrf.USBD.EVENTS_ENDEPIN[ep].Set(0) nrf.USBD.TASKS_STARTEPIN[ep].Set(1) } @@ -529,17 +548,3 @@ func enableEPIn(ep uint32) { epinen = epinen | (nrf.USBD_EPINEN_IN0 << ep) nrf.USBD.EPINEN.Set(epinen) } - -func strToUTF16LEDescriptor(in string) []byte { - runes := []rune(in) - encoded := utf16.Encode(runes) - size := (len(encoded) << 1) + 2 - out := make([]byte, size) - out[0] = byte(size) - out[1] = 0x03 - for i, value := range encoded { - out[(i<<1)+2] = byte(value & 0xff) - out[(i<<1)+3] = byte(value >> 8) - } - return out -} From 53ab2c29692c800f03935ad6c0efcbe415d4bdea Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Thu, 6 Feb 2020 21:42:54 -0500 Subject: [PATCH 07/11] make handleEndpoint not rely on global variable --- src/machine/usb_nrf52840.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/machine/usb_nrf52840.go b/src/machine/usb_nrf52840.go index 5fb1867ff8..9e0ddba21f 100644 --- a/src/machine/usb_nrf52840.go +++ b/src/machine/usb_nrf52840.go @@ -88,7 +88,7 @@ func exitCriticalSection() { func (usbcdc *USBCDC) Configure(config UARTConfig) { // enable IRQ usbcdc.interrupt = interrupt.New(nrf.IRQ_USBD, USB.handleInterrupt) - usbcdc.interrupt.SetPriority(0xDF) + usbcdc.interrupt.SetPriority(0xD0) usbcdc.interrupt.Enable() // enable USB @@ -202,7 +202,7 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { if nrf.USBD.EVENTS_ENDEPOUT[i].Get() > 0 { nrf.USBD.EVENTS_ENDEPOUT[i].Set(0) if i == usb_CDC_ENDPOINT_OUT { - handleEndpoint(uint32(i)) + usbcdc.handleEndpoint(uint32(i)) } exitCriticalSection() } @@ -518,13 +518,13 @@ func sendConfiguration(setup usbSetup) { } } -func handleEndpoint(ep uint32) { +func (usbcdc USBCDC) handleEndpoint(ep uint32) { // get data count := int(nrf.USBD.EPOUT[ep].AMOUNT.Get()) // move to ring buffer for i := 0; i < count; i++ { - USB.Receive(byte(udd_ep_out_cache_buffer[ep][i])) + usbcdc.Receive(byte(udd_ep_out_cache_buffer[ep][i])) } // set ready for next data From ddb311f362f4863263bf3591517b06a9c814cc68 Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Thu, 6 Feb 2020 22:01:17 -0500 Subject: [PATCH 08/11] set UART0 to be the USB-CDC on some nRF52840 boards This change allows the `board_*.go` files to choose which device will be UART0 and which will be UART1. For some it makes sense for the USB connection to be UART0 and for others it may not. --- src/machine/board_circuitplay_bluefruit.go | 6 ++++++ src/machine/board_nrf52840-mdk.go | 6 ++++++ src/machine/board_pca10056.go | 6 ++++++ src/machine/board_reelboard.go | 6 ++++++ src/machine/machine_nrf.go | 6 +++--- src/machine/machine_nrf51.go | 4 ++++ src/machine/machine_nrf52.go | 4 ++++ 7 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/machine/board_circuitplay_bluefruit.go b/src/machine/board_circuitplay_bluefruit.go index e33bc81aa7..8b8a4d7a28 100644 --- a/src/machine/board_circuitplay_bluefruit.go +++ b/src/machine/board_circuitplay_bluefruit.go @@ -56,6 +56,12 @@ const ( UART_RX_PIN = P0_30 // PORTB ) +// UART0 is the USB device and UART1 is the NRF UART +var ( + UART0 = USB + UART1 = NRF_UART0 +) + // I2C pins const ( SDA_PIN = P0_05 // I2C0 external diff --git a/src/machine/board_nrf52840-mdk.go b/src/machine/board_nrf52840-mdk.go index cd6e782766..39d2320c7e 100644 --- a/src/machine/board_nrf52840-mdk.go +++ b/src/machine/board_nrf52840-mdk.go @@ -18,6 +18,12 @@ const ( UART_RX_PIN Pin = 19 ) +// UART0 is the USB device and UART1 is the NRF UART +var ( + UART0 = USB + UART1 = NRF_UART0 +) + // I2C pins (unused) const ( SDA_PIN = NoPin diff --git a/src/machine/board_pca10056.go b/src/machine/board_pca10056.go index b9d3641403..41caf21465 100644 --- a/src/machine/board_pca10056.go +++ b/src/machine/board_pca10056.go @@ -28,6 +28,12 @@ const ( UART_RX_PIN Pin = 8 ) +// UART0 is the NRF UART and UART1 is the nRF52840's USB device +var ( + UART0 = NRF_UART0 + UART1 = USB +) + // ADC pins const ( ADC0 Pin = 3 diff --git a/src/machine/board_reelboard.go b/src/machine/board_reelboard.go index 77af29de15..195abb1b2e 100644 --- a/src/machine/board_reelboard.go +++ b/src/machine/board_reelboard.go @@ -35,6 +35,12 @@ const ( UART_RX_PIN Pin = 8 ) +// UART0 is the NRF UART and UART1 is the nRF52840's USB device +var ( + UART0 = NRF_UART0 + UART1 = USB +) + // I2C pins const ( SDA_PIN Pin = 26 diff --git a/src/machine/machine_nrf.go b/src/machine/machine_nrf.go index 0946356b54..e953de8947 100644 --- a/src/machine/machine_nrf.go +++ b/src/machine/machine_nrf.go @@ -66,8 +66,8 @@ type UART struct { // UART var ( - // UART0 is the hardware serial port on the NRF. - UART0 = UART{Buffer: NewRingBuffer()} + // NRF_UART0 is the hardware UART on the NRF SoC. + NRF_UART0 = UART{Buffer: NewRingBuffer()} ) // Configure the UART. @@ -88,7 +88,7 @@ func (uart UART) Configure(config UARTConfig) { nrf.UART0.INTENSET.Set(nrf.UART_INTENSET_RXDRDY_Msk) // Enable RX IRQ. - intr := interrupt.New(nrf.IRQ_UART0, UART0.handleInterrupt) + intr := interrupt.New(nrf.IRQ_UART0, NRF_UART0.handleInterrupt) intr.SetPriority(0xc0) // low priority intr.Enable() } diff --git a/src/machine/machine_nrf51.go b/src/machine/machine_nrf51.go index 2c9af2405d..416bc5bede 100644 --- a/src/machine/machine_nrf51.go +++ b/src/machine/machine_nrf51.go @@ -6,6 +6,10 @@ import ( "device/nrf" ) +var ( + UART0 = NRF_UART0 +) + func CPUFrequency() uint32 { return 16000000 } diff --git a/src/machine/machine_nrf52.go b/src/machine/machine_nrf52.go index b2181770a4..caa0c4e78f 100644 --- a/src/machine/machine_nrf52.go +++ b/src/machine/machine_nrf52.go @@ -7,6 +7,10 @@ import ( "unsafe" ) +var ( + UART0 = NRF_UART0 +) + func CPUFrequency() uint32 { return 64000000 } From a3dc24a2b3ea50f4e3c2196cafb21fa586df3444 Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Fri, 7 Feb 2020 21:29:55 -0500 Subject: [PATCH 09/11] work around issue with support for play.tinygo.org Create separate board_*_baremetal.go files for reelboard and pca10056 which will define UART0, in order to continue to allow support for these with the tinygo playground. Also switches the adafruit clue board to use the USB as UART0. --- src/machine/board_circuitplay_bluefruit.go | 3 +-- src/machine/board_clue_alpha.go | 5 +++++ src/machine/board_pca10056.go | 6 ------ src/machine/board_pca10056_baremetal.go | 8 ++++++++ src/machine/board_reelboard.go | 6 ------ src/machine/board_reelboard_baremetal.go | 8 ++++++++ 6 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 src/machine/board_pca10056_baremetal.go create mode 100644 src/machine/board_reelboard_baremetal.go diff --git a/src/machine/board_circuitplay_bluefruit.go b/src/machine/board_circuitplay_bluefruit.go index 8b8a4d7a28..f6c789775d 100644 --- a/src/machine/board_circuitplay_bluefruit.go +++ b/src/machine/board_circuitplay_bluefruit.go @@ -56,10 +56,9 @@ const ( UART_RX_PIN = P0_30 // PORTB ) -// UART0 is the USB device and UART1 is the NRF UART +// UART0 is the USB device var ( UART0 = USB - UART1 = NRF_UART0 ) // I2C pins diff --git a/src/machine/board_clue_alpha.go b/src/machine/board_clue_alpha.go index 1c5544f7f3..b2d07dc424 100644 --- a/src/machine/board_clue_alpha.go +++ b/src/machine/board_clue_alpha.go @@ -103,6 +103,11 @@ const ( UART_TX_PIN = D1 ) +// UART0 is the USB device +var ( + UART0 = USB +) + // I2C pins const ( SDA_PIN = D20 // I2C0 external diff --git a/src/machine/board_pca10056.go b/src/machine/board_pca10056.go index 41caf21465..b9d3641403 100644 --- a/src/machine/board_pca10056.go +++ b/src/machine/board_pca10056.go @@ -28,12 +28,6 @@ const ( UART_RX_PIN Pin = 8 ) -// UART0 is the NRF UART and UART1 is the nRF52840's USB device -var ( - UART0 = NRF_UART0 - UART1 = USB -) - // ADC pins const ( ADC0 Pin = 3 diff --git a/src/machine/board_pca10056_baremetal.go b/src/machine/board_pca10056_baremetal.go new file mode 100644 index 0000000000..75fcdee052 --- /dev/null +++ b/src/machine/board_pca10056_baremetal.go @@ -0,0 +1,8 @@ +// +build nrf52840,pca10056 + +package machine + +// UART0 is the NRF UART +var ( + UART0 = NRF_UART0 +) diff --git a/src/machine/board_reelboard.go b/src/machine/board_reelboard.go index 195abb1b2e..77af29de15 100644 --- a/src/machine/board_reelboard.go +++ b/src/machine/board_reelboard.go @@ -35,12 +35,6 @@ const ( UART_RX_PIN Pin = 8 ) -// UART0 is the NRF UART and UART1 is the nRF52840's USB device -var ( - UART0 = NRF_UART0 - UART1 = USB -) - // I2C pins const ( SDA_PIN Pin = 26 diff --git a/src/machine/board_reelboard_baremetal.go b/src/machine/board_reelboard_baremetal.go new file mode 100644 index 0000000000..3ae6507412 --- /dev/null +++ b/src/machine/board_reelboard_baremetal.go @@ -0,0 +1,8 @@ +// +build nrf52840,reelboard + +package machine + +// UART0 is the NRF UART +var ( + UART0 = NRF_UART0 +) From 6fd0c10e6fe10dc856de705ac8658b2a55815a4d Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Fri, 7 Feb 2020 22:45:14 -0500 Subject: [PATCH 10/11] remove UART1 from board nrf52840-mdk to make things consistent ...and to remove confusion with the UARTE1 device. --- src/machine/board_nrf52840-mdk.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/machine/board_nrf52840-mdk.go b/src/machine/board_nrf52840-mdk.go index 39d2320c7e..7e80e666d4 100644 --- a/src/machine/board_nrf52840-mdk.go +++ b/src/machine/board_nrf52840-mdk.go @@ -18,10 +18,9 @@ const ( UART_RX_PIN Pin = 19 ) -// UART0 is the USB device and UART1 is the NRF UART +// UART0 is the USB device var ( UART0 = USB - UART1 = NRF_UART0 ) // I2C pins (unused) From 1a09af53ae85662c547526691b42792893e6a407 Mon Sep 17 00:00:00 2001 From: Scott Yoder Date: Sun, 9 Feb 2020 20:15:54 -0500 Subject: [PATCH 11/11] properly receive and parse usb line info (baud rate, etc) --- src/machine/usb_nrf52840.go | 87 +++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/src/machine/usb_nrf52840.go b/src/machine/usb_nrf52840.go index 9e0ddba21f..c2ff3a1424 100644 --- a/src/machine/usb_nrf52840.go +++ b/src/machine/usb_nrf52840.go @@ -59,12 +59,13 @@ var ( (usb_ENDPOINT_TYPE_BULK | usbEndpointOut), (usb_ENDPOINT_TYPE_BULK | usbEndpointIn)} - usbConfiguration uint8 - usbSetInterface uint8 - usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} - epinen uint32 - epouten uint32 - easyDMABusy volatile.Register8 + usbConfiguration uint8 + usbSetInterface uint8 + usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} + epinen uint32 + epouten uint32 + easyDMABusy volatile.Register8 + epout0data_setlinecoding bool ) // enterCriticalSection is used to protect access to easyDMA - only one thing @@ -126,6 +127,12 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 { // done sending packet - either need to send another or enter status stage nrf.USBD.EVENTS_EP0DATADONE.Set(0) + if epout0data_setlinecoding { + nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0])))) + nrf.USBD.EPOUT[0].MAXCNT.Set(64) + nrf.USBD.TASKS_STARTEPOUT[0].Set(1) + return + } if sendOnEP0DATADONE.ptr != nil { // previous data was too big for one packet, so send a second sendViaEPIn( @@ -201,6 +208,15 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { for i := 0; i < len(endPoints); i++ { if nrf.USBD.EVENTS_ENDEPOUT[i].Get() > 0 { nrf.USBD.EVENTS_ENDEPOUT[i].Set(0) + if i == 0 && epout0data_setlinecoding { + epout0data_setlinecoding = false + count := int(nrf.USBD.SIZE.EPOUT[0].Get()) + if count >= 7 { + parseUSBLineInfo(udd_ep_out_cache_buffer[0][:count]) + checkShouldReset() + } + nrf.USBD.TASKS_EP0STATUS.Set(1) + } if i == usb_CDC_ENDPOINT_OUT { usbcdc.handleEndpoint(uint32(i)) } @@ -209,6 +225,13 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { } } +func parseUSBLineInfo(b []byte) { + usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 + usbLineInfo.bCharFormat = b[4] + usbLineInfo.bParityType = b[5] + usbLineInfo.bDataBits = b[6] +} + func parseUSBSetupRegisters() usbSetup { return usbSetup{ bmRequestType: uint8(nrf.USBD.BMREQUESTTYPE.Get()), @@ -241,9 +264,7 @@ func initEndpoint(ep, config uint32) { case usb_ENDPOINT_TYPE_CONTROL: enableEPIn(0) enableEPOut(0) - nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0])))) - nrf.USBD.EPOUT[0].MAXCNT.Set(64) - nrf.USBD.TASKS_STARTEPOUT[0].Set(1) + nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0) nrf.USBD.TASKS_EP0STATUS.Set(1) } } @@ -344,27 +365,14 @@ func cdcSetup(setup usbSetup) bool { if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { if setup.bRequest == usb_CDC_SET_LINE_CODING { - b := receiveUSBControlPacket() - if len(b) >= 7 { - usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 - usbLineInfo.bCharFormat = b[4] - usbLineInfo.bParityType = b[5] - usbLineInfo.bDataBits = b[6] - } + epout0data_setlinecoding = true + nrf.USBD.TASKS_EP0RCVOUT.Set(1) + return true } if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE { usbLineInfo.lineState = setup.wValueL - } - - if setup.bRequest == usb_CDC_SET_LINE_CODING || setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE { - // auto-reset into the bootloader - if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 { - // TODO: do we want to do this on the nRF52840? - // ResetProcessor() - } else { - // TODO: cancel any reset - } + checkShouldReset() nrf.USBD.TASKS_EP0STATUS.Set(1) } @@ -376,6 +384,12 @@ func cdcSetup(setup usbSetup) bool { return false } +func checkShouldReset() { + if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 { + // TODO: reset here + } +} + func sendUSBPacket(ep uint32, data []byte) { count := len(data) copy(udd_ep_in_cache_buffer[ep][:], data) @@ -391,27 +405,6 @@ func sendUSBPacket(ep uint32, data []byte) { ) } -func receiveUSBControlPacket() []byte { - // set byte count to zero - nrf.USBD.SIZE.EPOUT[0].Set(0) - nrf.USBD.TASKS_EP0RCVOUT.Set(1) - - // Wait until OUT transfer is ready. - timeout := 3000 - for nrf.USBD.EVENTS_EP0DATADONE.Get() == 0 { - timeout-- - if timeout == 0 { - return []byte{} - } - } - - // get data - bytesread := int(nrf.USBD.SIZE.EPOUT[0].Get()) - data := make([]byte, bytesread) - copy(data, udd_ep_out_cache_buffer[0][:]) - return data -} - // sendDescriptor creates and sends the various USB descriptor types that // can be requested by the host. func sendDescriptor(setup usbSetup) {