tinygo/src/machine/usb_nrf52840.go
Ayke van Laethem f20c932bb9 nrf52840: use higher priority for USB-CDC code
This ensures that stdout (println etc) keeps working in interrupts.

Generally you shouldn't print anything in an interrupt. However,
printing things for debugging is very useful and printing panic messages
can be critical when the code doesn't work for some reason.
2020-09-09 17:15:50 +02:00

439 строки
11 КиБ
Go

// +build nrf52840
package machine
import (
"device/arm"
"device/nrf"
"runtime/interrupt"
"runtime/volatile"
"unsafe"
)
// USBCDC is the USB CDC aka serial over USB interface on the nRF52840
type USBCDC struct {
Buffer *RingBuffer
interrupt interrupt.Interrupt
}
// 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 {
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,
)
}
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 (
USB = USBCDC{Buffer: NewRingBuffer()}
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
easyDMABusy volatile.Register8
epout0data_setlinecoding bool
)
// 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. Make sure this is higher than the SWI2 interrupt handler so
// that it is possible to print to the console from a BLE interrupt. You
// shouldn't generally do that but it is useful for debugging and panic
// logging.
usbcdc.interrupt = interrupt.New(nrf.IRQ_USBD, USB.handleInterrupt)
usbcdc.interrupt.SetPriority(0x40) // interrupt priority 2 (lower number means more important)
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_EPDATA |
nrf.USBD_INTENSET_EP0DATADONE |
nrf.USBD_INTENSET_USBEVENT |
nrf.USBD_INTENSET_EP0SETUP,
)
nrf.USBD.USBPULLUP.Set(0)
}
func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) {
// USBD ready event
if nrf.USBD.EVENTS_USBEVENT.Get() == 1 {
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)
// Enable Setup-Received interrupt
nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_EP0SETUP)
nrf.USBD.USBPULLUP.Set(1)
usbConfiguration = 0
}
nrf.USBD.EVENTCAUSE.Set(0)
}
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(
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)
}
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 {
nrf.USBD.EVENTS_EPDATA.Set(0)
epDataStatus := nrf.USBD.EPDATASTATUS.Get()
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&(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:
// 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)
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))
}
exitCriticalSection()
}
}
}
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()),
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:
nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0 << ep)
nrf.USBD.SIZE.EPOUT[ep].Set(0)
enableEPOut(ep)
case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut:
nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0 << ep)
nrf.USBD.SIZE.EPOUT[ep].Set(0)
enableEPOut(ep)
case usb_ENDPOINT_TYPE_BULK | usbEndpointIn:
enableEPIn(ep)
case usb_ENDPOINT_TYPE_CONTROL:
enableEPIn(0)
enableEPOut(0)
nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0)
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
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 {
nrf.USBD.TASKS_EP0STATUS.Set(1)
for i := 1; i < len(endPoints); i++ {
initEndpoint(uint32(i), endPoints[i])
}
usbConfiguration = setup.wValueL
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 {
epout0data_setlinecoding = true
nrf.USBD.TASKS_EP0RCVOUT.Set(1)
return true
}
if setup.bRequest == usb_CDC_SET_CONTROL_LINE_STATE {
usbLineInfo.lineState = setup.wValueL
checkShouldReset()
nrf.USBD.TASKS_EP0STATUS.Set(1)
}
if setup.bRequest == usb_CDC_SEND_BREAK {
nrf.USBD.TASKS_EP0STATUS.Set(1)
}
return true
}
return false
}
//go:noinline
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 (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++ {
usbcdc.Receive(byte(udd_ep_out_cache_buffer[ep][i]))
}
// set ready for next data
nrf.USBD.SIZE.EPOUT[ep].Set(0)
}
func sendZlp() {
nrf.USBD.TASKS_EP0STATUS.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.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)
}