diff --git a/src/examples/usb-midi/main.go b/src/examples/usb-midi/main.go new file mode 100644 index 000000000..75e89a23c --- /dev/null +++ b/src/examples/usb-midi/main.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + "machine" + "machine/usb/midi" + "time" +) + +func main() { + led := machine.LED + led.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + button := machine.BUTTON + button.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + + m := midi.New() + m.SetCallback(func(b []byte) { + led.Set(!led.Get()) + fmt.Printf("% X\r\n", b) + m.Write(b) + }) + + prev := true + chords := []struct { + name string + keys []byte + }{ + {name: "C ", keys: []byte{60, 64, 67}}, + {name: "G ", keys: []byte{55, 59, 62}}, + {name: "Am", keys: []byte{57, 60, 64}}, + {name: "F ", keys: []byte{53, 57, 60}}, + } + index := 0 + + for { + current := button.Get() + if prev != current { + led.Set(current) + if current { + for _, c := range chords[index].keys { + m.Write([]byte{0x08, 0x80, c, 0x40}) + } + index = (index + 1) % len(chords) + } else { + for _, c := range chords[index].keys { + m.Write([]byte{0x09, 0x90, c, 0x40}) + } + } + prev = current + } + time.Sleep(10 * time.Millisecond) + } +} diff --git a/src/machine/machine_atsamd21.go b/src/machine/machine_atsamd21.go index d88190254..75b014226 100644 --- a/src/machine/machine_atsamd21.go +++ b/src/machine/machine_atsamd21.go @@ -13,7 +13,6 @@ import ( "device/sam" "errors" "runtime/interrupt" - "runtime/volatile" "unsafe" ) @@ -1735,926 +1734,6 @@ func (tcc *TCC) Set(channel uint8, value uint32) { } } -// USBCDC is the USB CDC aka serial over USB interface on the SAMD21. -type USBCDC struct { - Buffer *RingBuffer - TxIdx volatile.Register8 - waitTxc bool - waitTxcRetryCount uint8 - sent bool - configured bool -} - -var ( - USB = &USBCDC{Buffer: NewRingBuffer()} - waitHidTxc bool -) - -const ( - usbcdcTxSizeMask uint8 = 0x3F - usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask - usbcdcTxBank1st uint8 = 0x00 - usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1 - usbcdcTxMaxRetriesAllowed uint8 = 5 -) - -// Flush flushes buffered data. -func (usbcdc *USBCDC) Flush() error { - if usbLineInfo.lineState > 0 { - idx := usbcdc.TxIdx.Get() - sz := idx & usbcdcTxSizeMask - bk := idx & usbcdcTxBankMask - if 0 < sz { - - if usbcdc.waitTxc { - // waiting for the next flush(), because the transmission is not complete - usbcdc.waitTxcRetryCount++ - return nil - } - usbcdc.waitTxc = true - usbcdc.waitTxcRetryCount = 0 - - // set the data - usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk])))) - if bk == usbcdcTxBank1st { - usbcdc.TxIdx.Set(usbcdcTxBank2nd) - } else { - usbcdc.TxIdx.Set(usbcdcTxBank1st) - } - - // clean multi packet size of bytes already sent - usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) - - // set count of bytes to be sent - usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - - // clear transfer complete flag - setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1) - - // send data by setting bank ready - setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSSET_BK1RDY) - usbcdc.sent = true - } - } - return nil -} - -// 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 { - ok := false - for { - mask := interrupt.Disable() - - idx := usbcdc.TxIdx.Get() - if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask { - udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c - usbcdc.TxIdx.Set(idx + 1) - ok = true - } - - interrupt.Restore(mask) - - if ok { - break - } else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount { - mask := interrupt.Disable() - usbcdc.waitTxc = false - usbcdc.waitTxcRetryCount = 0 - usbcdc.TxIdx.Set(0) - usbLineInfo.lineState = 0 - interrupt.Restore(mask) - break - } else { - mask := interrupt.Disable() - if usbcdc.sent { - if usbcdc.waitTxc { - if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) != 0 { - setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY) - setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_EPINTFLAG_TRCPT1) - usbcdc.waitTxc = false - usbcdc.Flush() - } - } else { - usbcdc.Flush() - } - } - interrupt.Restore(mask) - } - } - } - - 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 -} - -const ( - // these are SAMD21 specific. - usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0 - usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF - - usb_DEVICE_PCKSIZE_SIZE_Pos = 28 - usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7 - - usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14 - usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF -) - -var ( - usbEndpointDescriptors [8]usbDeviceDescriptor - - udd_ep_in_cache_buffer [7][128]uint8 - udd_ep_out_cache_buffer [7][128]uint8 - - 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} -) - -// Configure the USB CDC interface. The config is here for compatibility with the UART interface. -func (usbcdc *USBCDC) Configure(config UARTConfig) { - // reset USB interface - sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST) - for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) || - sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) { - } - - sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors)))) - - // configure pins - USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom}) - USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom}) - - // performs pad calibration from store fuses - handlePadCalibration() - - // run in standby - sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY) - - // set full speed - sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos) - - // attach - sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) - - // enable interrupt for end of reset - sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST) - - // enable interrupt for start of frame - sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF) - - // enable USB - sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE) - - // enable IRQ - intr := interrupt.New(sam.IRQ_USB, handleUSB) - intr.Enable() - - usbcdc.configured = true -} - -// Configured returns whether usbcdc is configured or not. -func (usbcdc *USBCDC) Configured() bool { - return usbcdc.configured -} - -func handlePadCalibration() { - // Load Pad Calibration data from non-volatile memory - // This requires registers that are not included in the SVD file. - // Modeled after defines from samd21g18a.h and nvmctrl.h: - // - // #define NVMCTRL_OTP4 0x00806020 - // - // #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4) - // #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */ - // #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos) - // #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos))) - - // #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4) - // #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */ - // #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos) - // #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos))) - - // #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4) - // #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */ - // #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos) - // #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos))) - // - fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4)) - calibTransN := uint16(fuse>>13) & uint16(0x1f) - calibTransP := uint16(fuse>>18) & uint16(0x1f) - calibTrim := uint16(fuse>>23) & uint16(0x7) - - if calibTransN == 0x1f { - calibTransN = 5 - } - sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos) - - if calibTransP == 0x1f { - calibTransP = 29 - } - sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos) - - if calibTrim == 0x7 { - calibTrim = 3 - } - sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos) -} - -func handleUSB(intr interrupt.Interrupt) { - // reset all interrupt flags - flags := sam.USB_DEVICE.INTFLAG.Get() - sam.USB_DEVICE.INTFLAG.Set(flags) - - // End of reset - if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 { - // Configure control endpoint - initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL) - - // Enable Setup-Received interrupt - setEPINTENSET(0, sam.USB_DEVICE_EPINTENSET_RXSTP) - - usbConfiguration = 0 - - // ack the End-Of-Reset interrupt - sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST) - } - - // Start of frame - if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 { - USB.Flush() - // if you want to blink LED showing traffic, this would be the place... - if hidCallback != nil && !waitHidTxc { - hidCallback() - } - } - - // Endpoint 0 Setup interrupt - if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_RXSTP > 0 { - // ack setup received - setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_RXSTP) - - // parse setup - setup := newUSBSetup(udd_ep_out_cache_buffer[0][:]) - - // Clear the Bank 0 ready flag on Control OUT - setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) - usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - - ok := false - if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD { - // Standard Requests - ok = handleStandardSetup(setup) - } else { - // Class Interface Requests - if setup.wIndex == usb_CDC_ACM_INTERFACE { - ok = cdcSetup(setup) - } else if setup.bmRequestType == usb_SET_REPORT_TYPE && setup.bRequest == usb_SET_IDLE { - sendZlp() - ok = true - } - } - - if ok { - // set Bank1 ready - setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY) - } else { - // Stall endpoint - setEPSTATUSSET(0, sam.USB_DEVICE_EPINTFLAG_STALL1) - } - - if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_STALL1 > 0 { - // ack the stall - setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_STALL1) - - // clear stall request - setEPINTENCLR(0, sam.USB_DEVICE_EPINTENCLR_STALL1) - } - } - - // Now the actual transfer handlers, ignore endpoint number 0 (setup) - var i uint32 - for i = 1; i < uint32(len(endPoints)); i++ { - // Check if endpoint has a pending interrupt - epFlags := getEPINTFLAG(i) - if (epFlags&sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 || - (epFlags&sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 { - switch i { - case usb_CDC_ENDPOINT_OUT: - handleEndpoint(i) - setEPINTFLAG(i, epFlags) - case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM: - setEPSTATUSCLR(i, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY) - setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1) - - if i == usb_CDC_ENDPOINT_IN { - USB.waitTxc = false - } - case usb_HID_ENDPOINT_IN: - setEPINTFLAG(i, sam.USB_DEVICE_EPINTFLAG_TRCPT1) - waitHidTxc = false - } - } - } -} - -func initEndpoint(ep, config uint32) { - switch config { - case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn: - // set packet size - usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) - - // set data buffer address - usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) - - // set endpoint type - setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos)) - - case usb_ENDPOINT_TYPE_BULK | usbEndpointOut: - // set packet size - usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) - - // set data buffer address - usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep])))) - - // set endpoint type - setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos)) - - // receive interrupts when current transfer complete - setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0) - - // set byte count to zero, we have not received anything yet - usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - - // ready for next transfer - setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) - - case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut: - // TODO: not really anything, seems like... - - case usb_ENDPOINT_TYPE_BULK | usbEndpointIn: - // set packet size - usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) - - // set data buffer address - usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) - - // set endpoint type - setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos)) - - // NAK on endpoint IN, the bank is not yet filled in. - setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY) - - case usb_ENDPOINT_TYPE_CONTROL: - // Control OUT - // set packet size - usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) - - // set data buffer address - usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep])))) - - // set endpoint type - setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<> 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[:], setup.wLength) - return true - } - } - - if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { - if setup.bRequest == usb_CDC_SET_LINE_CODING { - b, err := receiveUSBControlPacket() - if err != nil { - return false - } - - 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 { - ResetProcessor() - } else { - // TODO: cancel any reset - } - sendZlp() - } - - if setup.bRequest == usb_CDC_SEND_BREAK { - // TODO: something with this value? - // breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL; - // return false; - sendZlp() - } - return true - } - return false -} - -// SendUSBHIDPacket sends a packet for USBHID (interrupt / in). -func SendUSBHIDPacket(ep uint32, data []byte) bool { - if waitHidTxc { - return false - } - sendUSBPacket(ep, data, 0) - - // clear transfer complete flag - setEPINTFLAG(ep, sam.USB_DEVICE_EPINTFLAG_TRCPT1) - - // send data by setting bank ready - setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_BK1RDY) - - waitHidTxc = true - - return true -} - -//go:noinline -func sendUSBPacket(ep uint32, data []byte, maxsize uint16) { - l := uint16(len(data)) - if 0 < maxsize && maxsize < l { - l = maxsize - } - copy(udd_ep_in_cache_buffer[ep][:], data[:l]) - - // Set endpoint address for sending data - usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) - - // clear multi-packet size which is total bytes already sent - usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) - - // set byte count, which is total number of bytes to be sent - usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) -} - -func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) { - var b [cdcLineInfoSize]byte - - // address - usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0])))) - - // set byte count to zero - usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - - // set ready for next data - setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) - - // Wait until OUT transfer is ready. - timeout := 300000 - for (getEPSTATUS(0) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 { - timeout-- - if timeout == 0 { - return b, errUSBCDCReadTimeout - } - } - - // Wait until OUT transfer is completed. - timeout = 300000 - for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 { - timeout-- - if timeout == 0 { - return b, errUSBCDCReadTimeout - } - } - - // get data - bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >> - usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) - - if bytesread != cdcLineInfoSize { - return b, errUSBCDCBytesRead - } - - copy(b[:7], udd_ep_out_cache_buffer[0][:7]) - - return b, nil -} - -func handleEndpoint(ep uint32) { - // get data - count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >> - usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) - - // move to ring buffer - for i := 0; i < count; i++ { - USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF))) - } - - // set byte count to zero - usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - - // set multi packet size to 64 - usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) - - // set ready for next data - setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) -} - -func sendZlp() { - usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) -} - -func epPacketSize(size uint16) uint32 { - switch size { - case 8: - return 0 - case 16: - return 1 - case 32: - return 2 - case 64: - return 3 - case 128: - return 4 - case 256: - return 5 - case 512: - return 6 - case 1023: - return 7 - default: - return 0 - } -} - -func getEPCFG(ep uint32) uint8 { - switch ep { - case 0: - return sam.USB_DEVICE.EPCFG0.Get() - case 1: - return sam.USB_DEVICE.EPCFG1.Get() - case 2: - return sam.USB_DEVICE.EPCFG2.Get() - case 3: - return sam.USB_DEVICE.EPCFG3.Get() - case 4: - return sam.USB_DEVICE.EPCFG4.Get() - case 5: - return sam.USB_DEVICE.EPCFG5.Get() - case 6: - return sam.USB_DEVICE.EPCFG6.Get() - case 7: - return sam.USB_DEVICE.EPCFG7.Get() - default: - return 0 - } -} - -func setEPCFG(ep uint32, val uint8) { - switch ep { - case 0: - sam.USB_DEVICE.EPCFG0.Set(val) - case 1: - sam.USB_DEVICE.EPCFG1.Set(val) - case 2: - sam.USB_DEVICE.EPCFG2.Set(val) - case 3: - sam.USB_DEVICE.EPCFG3.Set(val) - case 4: - sam.USB_DEVICE.EPCFG4.Set(val) - case 5: - sam.USB_DEVICE.EPCFG5.Set(val) - case 6: - sam.USB_DEVICE.EPCFG6.Set(val) - case 7: - sam.USB_DEVICE.EPCFG7.Set(val) - default: - return - } -} - -func setEPSTATUSCLR(ep uint32, val uint8) { - switch ep { - case 0: - sam.USB_DEVICE.EPSTATUSCLR0.Set(val) - case 1: - sam.USB_DEVICE.EPSTATUSCLR1.Set(val) - case 2: - sam.USB_DEVICE.EPSTATUSCLR2.Set(val) - case 3: - sam.USB_DEVICE.EPSTATUSCLR3.Set(val) - case 4: - sam.USB_DEVICE.EPSTATUSCLR4.Set(val) - case 5: - sam.USB_DEVICE.EPSTATUSCLR5.Set(val) - case 6: - sam.USB_DEVICE.EPSTATUSCLR6.Set(val) - case 7: - sam.USB_DEVICE.EPSTATUSCLR7.Set(val) - default: - return - } -} - -func setEPSTATUSSET(ep uint32, val uint8) { - switch ep { - case 0: - sam.USB_DEVICE.EPSTATUSSET0.Set(val) - case 1: - sam.USB_DEVICE.EPSTATUSSET1.Set(val) - case 2: - sam.USB_DEVICE.EPSTATUSSET2.Set(val) - case 3: - sam.USB_DEVICE.EPSTATUSSET3.Set(val) - case 4: - sam.USB_DEVICE.EPSTATUSSET4.Set(val) - case 5: - sam.USB_DEVICE.EPSTATUSSET5.Set(val) - case 6: - sam.USB_DEVICE.EPSTATUSSET6.Set(val) - case 7: - sam.USB_DEVICE.EPSTATUSSET7.Set(val) - default: - return - } -} - -func getEPSTATUS(ep uint32) uint8 { - switch ep { - case 0: - return sam.USB_DEVICE.EPSTATUS0.Get() - case 1: - return sam.USB_DEVICE.EPSTATUS1.Get() - case 2: - return sam.USB_DEVICE.EPSTATUS2.Get() - case 3: - return sam.USB_DEVICE.EPSTATUS3.Get() - case 4: - return sam.USB_DEVICE.EPSTATUS4.Get() - case 5: - return sam.USB_DEVICE.EPSTATUS5.Get() - case 6: - return sam.USB_DEVICE.EPSTATUS6.Get() - case 7: - return sam.USB_DEVICE.EPSTATUS7.Get() - default: - return 0 - } -} - -func getEPINTFLAG(ep uint32) uint8 { - switch ep { - case 0: - return sam.USB_DEVICE.EPINTFLAG0.Get() - case 1: - return sam.USB_DEVICE.EPINTFLAG1.Get() - case 2: - return sam.USB_DEVICE.EPINTFLAG2.Get() - case 3: - return sam.USB_DEVICE.EPINTFLAG3.Get() - case 4: - return sam.USB_DEVICE.EPINTFLAG4.Get() - case 5: - return sam.USB_DEVICE.EPINTFLAG5.Get() - case 6: - return sam.USB_DEVICE.EPINTFLAG6.Get() - case 7: - return sam.USB_DEVICE.EPINTFLAG7.Get() - default: - return 0 - } -} - -func setEPINTFLAG(ep uint32, val uint8) { - switch ep { - case 0: - sam.USB_DEVICE.EPINTFLAG0.Set(val) - case 1: - sam.USB_DEVICE.EPINTFLAG1.Set(val) - case 2: - sam.USB_DEVICE.EPINTFLAG2.Set(val) - case 3: - sam.USB_DEVICE.EPINTFLAG3.Set(val) - case 4: - sam.USB_DEVICE.EPINTFLAG4.Set(val) - case 5: - sam.USB_DEVICE.EPINTFLAG5.Set(val) - case 6: - sam.USB_DEVICE.EPINTFLAG6.Set(val) - case 7: - sam.USB_DEVICE.EPINTFLAG7.Set(val) - default: - return - } -} - -func setEPINTENCLR(ep uint32, val uint8) { - switch ep { - case 0: - sam.USB_DEVICE.EPINTENCLR0.Set(val) - case 1: - sam.USB_DEVICE.EPINTENCLR1.Set(val) - case 2: - sam.USB_DEVICE.EPINTENCLR2.Set(val) - case 3: - sam.USB_DEVICE.EPINTENCLR3.Set(val) - case 4: - sam.USB_DEVICE.EPINTENCLR4.Set(val) - case 5: - sam.USB_DEVICE.EPINTENCLR5.Set(val) - case 6: - sam.USB_DEVICE.EPINTENCLR6.Set(val) - case 7: - sam.USB_DEVICE.EPINTENCLR7.Set(val) - default: - return - } -} - -func setEPINTENSET(ep uint32, val uint8) { - switch ep { - case 0: - sam.USB_DEVICE.EPINTENSET0.Set(val) - case 1: - sam.USB_DEVICE.EPINTENSET1.Set(val) - case 2: - sam.USB_DEVICE.EPINTENSET2.Set(val) - case 3: - sam.USB_DEVICE.EPINTENSET3.Set(val) - case 4: - sam.USB_DEVICE.EPINTENSET4.Set(val) - case 5: - sam.USB_DEVICE.EPINTENSET5.Set(val) - case 6: - sam.USB_DEVICE.EPINTENSET6.Set(val) - case 7: - sam.USB_DEVICE.EPINTENSET7.Set(val) - default: - return - } -} - // ResetProcessor should perform a system reset in preperation // to switch to the bootloader to flash new firmware. func ResetProcessor() { diff --git a/src/machine/machine_atsamd21_usb.go b/src/machine/machine_atsamd21_usb.go new file mode 100644 index 000000000..7b8f39e3a --- /dev/null +++ b/src/machine/machine_atsamd21_usb.go @@ -0,0 +1,628 @@ +//go:build sam && atsamd21 +// +build sam,atsamd21 + +package machine + +import ( + "device/sam" + "runtime/interrupt" + "unsafe" +) + +const ( + // these are SAMD21 specific. + usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0 + usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF + + usb_DEVICE_PCKSIZE_SIZE_Pos = 28 + usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7 + + usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14 + usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF +) + +// Configure the USB peripheral. The config is here for compatibility with the UART interface. +func (dev *USBDevice) Configure(config UARTConfig) { + // reset USB interface + sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST) + for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) || + sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) { + } + + sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors)))) + + // configure pins + USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom}) + USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom}) + + // performs pad calibration from store fuses + handlePadCalibration() + + // run in standby + sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY) + + // set full speed + sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos) + + // attach + sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) + + // enable interrupt for end of reset + sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST) + + // enable interrupt for start of frame + sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF) + + // enable USB + sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE) + + // enable IRQ + interrupt.New(sam.IRQ_USB, handleUSBIRQ).Enable() +} + +func handlePadCalibration() { + // Load Pad Calibration data from non-volatile memory + // This requires registers that are not included in the SVD file. + // Modeled after defines from samd21g18a.h and nvmctrl.h: + // + // #define NVMCTRL_OTP4 0x00806020 + // + // #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4) + // #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */ + // #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos) + // #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos))) + + // #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4) + // #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */ + // #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos) + // #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos))) + + // #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4) + // #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */ + // #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos) + // #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos))) + // + fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4)) + calibTransN := uint16(fuse>>13) & uint16(0x1f) + calibTransP := uint16(fuse>>18) & uint16(0x1f) + calibTrim := uint16(fuse>>23) & uint16(0x7) + + if calibTransN == 0x1f { + calibTransN = 5 + } + sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos) + + if calibTransP == 0x1f { + calibTransP = 29 + } + sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos) + + if calibTrim == 0x7 { + calibTrim = 3 + } + sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos) +} + +func handleUSBIRQ(intr interrupt.Interrupt) { + // reset all interrupt flags + flags := sam.USB_DEVICE.INTFLAG.Get() + sam.USB_DEVICE.INTFLAG.Set(flags) + + // End of reset + if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 { + // Configure control endpoint + initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL) + + usbConfiguration = 0 + + // ack the End-Of-Reset interrupt + sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST) + } + + // Start of frame + if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 { + // if you want to blink LED showing traffic, this would be the place... + } + + // Endpoint 0 Setup interrupt + if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_RXSTP > 0 { + // ack setup received + setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_RXSTP) + + // parse setup + setup := newUSBSetup(udd_ep_out_cache_buffer[0][:]) + + // Clear the Bank 0 ready flag on Control OUT + setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) + usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) + + ok := false + if (setup.BmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD { + // Standard Requests + ok = handleStandardSetup(setup) + } else { + // Class Interface Requests + if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil { + ok = callbackUSBSetup[setup.WIndex](setup) + } + } + + if ok { + // set Bank1 ready + setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY) + } else { + // Stall endpoint + setEPSTATUSSET(0, sam.USB_DEVICE_EPINTFLAG_STALL1) + } + + if getEPINTFLAG(0)&sam.USB_DEVICE_EPINTFLAG_STALL1 > 0 { + // ack the stall + setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_STALL1) + + // clear stall request + setEPINTENCLR(0, sam.USB_DEVICE_EPINTENCLR_STALL1) + } + } + + // Now the actual transfer handlers, ignore endpoint number 0 (setup) + var i uint32 + for i = 1; i < uint32(len(endPoints)); i++ { + // Check if endpoint has a pending interrupt + epFlags := getEPINTFLAG(i) + setEPINTFLAG(i, epFlags) + if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 { + buf := handleEndpointRx(i) + if callbackUSBRx[i] != nil { + callbackUSBRx[i](buf) + } + } else if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT1) > 0 { + if callbackUSBTx[i] != nil { + callbackUSBTx[i]() + } + } + } +} + +func initEndpoint(ep, config uint32) { + switch config { + case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn: + // set packet size + usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) + + // set endpoint type + setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos)) + + setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT1) + + case usb_ENDPOINT_TYPE_BULK | usbEndpointOut: + // set packet size + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep])))) + + // set endpoint type + setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE0_Pos)) + + // receive interrupts when current transfer complete + setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0) + + // set byte count to zero, we have not received anything yet + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) + + // ready for next transfer + setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) + + case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut: + // TODO: not really anything, seems like... + + case usb_ENDPOINT_TYPE_BULK | usbEndpointIn: + // set packet size + usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) + + // set endpoint type + setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_EPCFG_EPTYPE1_Pos)) + + // NAK on endpoint IN, the bank is not yet filled in. + setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK1RDY) + + setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT1) + + case usb_ENDPOINT_TYPE_CONTROL: + // Control OUT + // set packet size + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep])))) + + // set endpoint type + setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<> + usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) + + if bytesread != cdcLineInfoSize { + return b, errUSBCDCBytesRead + } + + copy(b[:7], udd_ep_out_cache_buffer[0][:7]) + + return b, nil +} + +func handleEndpointRx(ep uint32) []byte { + // get data + count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >> + usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) + + // move to ring buffer + buf := make([]byte, count) + copy(buf, udd_ep_out_cache_buffer[ep][:]) + + // set byte count to zero + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) + + // set multi packet size to 64 + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) + + // set ready for next data + setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) + + return buf[:count] +} + +func SendZlp() { + usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) +} + +func epPacketSize(size uint16) uint32 { + switch size { + case 8: + return 0 + case 16: + return 1 + case 32: + return 2 + case 64: + return 3 + case 128: + return 4 + case 256: + return 5 + case 512: + return 6 + case 1023: + return 7 + default: + return 0 + } +} + +func getEPCFG(ep uint32) uint8 { + switch ep { + case 0: + return sam.USB_DEVICE.EPCFG0.Get() + case 1: + return sam.USB_DEVICE.EPCFG1.Get() + case 2: + return sam.USB_DEVICE.EPCFG2.Get() + case 3: + return sam.USB_DEVICE.EPCFG3.Get() + case 4: + return sam.USB_DEVICE.EPCFG4.Get() + case 5: + return sam.USB_DEVICE.EPCFG5.Get() + case 6: + return sam.USB_DEVICE.EPCFG6.Get() + case 7: + return sam.USB_DEVICE.EPCFG7.Get() + default: + return 0 + } +} + +func setEPCFG(ep uint32, val uint8) { + switch ep { + case 0: + sam.USB_DEVICE.EPCFG0.Set(val) + case 1: + sam.USB_DEVICE.EPCFG1.Set(val) + case 2: + sam.USB_DEVICE.EPCFG2.Set(val) + case 3: + sam.USB_DEVICE.EPCFG3.Set(val) + case 4: + sam.USB_DEVICE.EPCFG4.Set(val) + case 5: + sam.USB_DEVICE.EPCFG5.Set(val) + case 6: + sam.USB_DEVICE.EPCFG6.Set(val) + case 7: + sam.USB_DEVICE.EPCFG7.Set(val) + default: + return + } +} + +func setEPSTATUSCLR(ep uint32, val uint8) { + switch ep { + case 0: + sam.USB_DEVICE.EPSTATUSCLR0.Set(val) + case 1: + sam.USB_DEVICE.EPSTATUSCLR1.Set(val) + case 2: + sam.USB_DEVICE.EPSTATUSCLR2.Set(val) + case 3: + sam.USB_DEVICE.EPSTATUSCLR3.Set(val) + case 4: + sam.USB_DEVICE.EPSTATUSCLR4.Set(val) + case 5: + sam.USB_DEVICE.EPSTATUSCLR5.Set(val) + case 6: + sam.USB_DEVICE.EPSTATUSCLR6.Set(val) + case 7: + sam.USB_DEVICE.EPSTATUSCLR7.Set(val) + default: + return + } +} + +func setEPSTATUSSET(ep uint32, val uint8) { + switch ep { + case 0: + sam.USB_DEVICE.EPSTATUSSET0.Set(val) + case 1: + sam.USB_DEVICE.EPSTATUSSET1.Set(val) + case 2: + sam.USB_DEVICE.EPSTATUSSET2.Set(val) + case 3: + sam.USB_DEVICE.EPSTATUSSET3.Set(val) + case 4: + sam.USB_DEVICE.EPSTATUSSET4.Set(val) + case 5: + sam.USB_DEVICE.EPSTATUSSET5.Set(val) + case 6: + sam.USB_DEVICE.EPSTATUSSET6.Set(val) + case 7: + sam.USB_DEVICE.EPSTATUSSET7.Set(val) + default: + return + } +} + +func getEPSTATUS(ep uint32) uint8 { + switch ep { + case 0: + return sam.USB_DEVICE.EPSTATUS0.Get() + case 1: + return sam.USB_DEVICE.EPSTATUS1.Get() + case 2: + return sam.USB_DEVICE.EPSTATUS2.Get() + case 3: + return sam.USB_DEVICE.EPSTATUS3.Get() + case 4: + return sam.USB_DEVICE.EPSTATUS4.Get() + case 5: + return sam.USB_DEVICE.EPSTATUS5.Get() + case 6: + return sam.USB_DEVICE.EPSTATUS6.Get() + case 7: + return sam.USB_DEVICE.EPSTATUS7.Get() + default: + return 0 + } +} + +func getEPINTFLAG(ep uint32) uint8 { + switch ep { + case 0: + return sam.USB_DEVICE.EPINTFLAG0.Get() + case 1: + return sam.USB_DEVICE.EPINTFLAG1.Get() + case 2: + return sam.USB_DEVICE.EPINTFLAG2.Get() + case 3: + return sam.USB_DEVICE.EPINTFLAG3.Get() + case 4: + return sam.USB_DEVICE.EPINTFLAG4.Get() + case 5: + return sam.USB_DEVICE.EPINTFLAG5.Get() + case 6: + return sam.USB_DEVICE.EPINTFLAG6.Get() + case 7: + return sam.USB_DEVICE.EPINTFLAG7.Get() + default: + return 0 + } +} + +func setEPINTFLAG(ep uint32, val uint8) { + switch ep { + case 0: + sam.USB_DEVICE.EPINTFLAG0.Set(val) + case 1: + sam.USB_DEVICE.EPINTFLAG1.Set(val) + case 2: + sam.USB_DEVICE.EPINTFLAG2.Set(val) + case 3: + sam.USB_DEVICE.EPINTFLAG3.Set(val) + case 4: + sam.USB_DEVICE.EPINTFLAG4.Set(val) + case 5: + sam.USB_DEVICE.EPINTFLAG5.Set(val) + case 6: + sam.USB_DEVICE.EPINTFLAG6.Set(val) + case 7: + sam.USB_DEVICE.EPINTFLAG7.Set(val) + default: + return + } +} + +func setEPINTENCLR(ep uint32, val uint8) { + switch ep { + case 0: + sam.USB_DEVICE.EPINTENCLR0.Set(val) + case 1: + sam.USB_DEVICE.EPINTENCLR1.Set(val) + case 2: + sam.USB_DEVICE.EPINTENCLR2.Set(val) + case 3: + sam.USB_DEVICE.EPINTENCLR3.Set(val) + case 4: + sam.USB_DEVICE.EPINTENCLR4.Set(val) + case 5: + sam.USB_DEVICE.EPINTENCLR5.Set(val) + case 6: + sam.USB_DEVICE.EPINTENCLR6.Set(val) + case 7: + sam.USB_DEVICE.EPINTENCLR7.Set(val) + default: + return + } +} + +func setEPINTENSET(ep uint32, val uint8) { + switch ep { + case 0: + sam.USB_DEVICE.EPINTENSET0.Set(val) + case 1: + sam.USB_DEVICE.EPINTENSET1.Set(val) + case 2: + sam.USB_DEVICE.EPINTENSET2.Set(val) + case 3: + sam.USB_DEVICE.EPINTENSET3.Set(val) + case 4: + sam.USB_DEVICE.EPINTENSET4.Set(val) + case 5: + sam.USB_DEVICE.EPINTENSET5.Set(val) + case 6: + sam.USB_DEVICE.EPINTENSET6.Set(val) + case 7: + sam.USB_DEVICE.EPINTENSET7.Set(val) + default: + return + } +} diff --git a/src/machine/machine_atsamd51.go b/src/machine/machine_atsamd51.go index db2fea71e..4f2968329 100644 --- a/src/machine/machine_atsamd51.go +++ b/src/machine/machine_atsamd51.go @@ -13,7 +13,6 @@ import ( "device/sam" "errors" "runtime/interrupt" - "runtime/volatile" "unsafe" ) @@ -1975,759 +1974,6 @@ func (tcc *TCC) Set(channel uint8, value uint32) { } } -// USBCDC is the USB CDC aka serial over USB interface on the SAMD21. -type USBCDC struct { - Buffer *RingBuffer - TxIdx volatile.Register8 - waitTxc bool - waitTxcRetryCount uint8 - sent bool - configured bool -} - -var ( - // USB is a USB CDC interface. - USB = &USBCDC{Buffer: NewRingBuffer()} - waitHidTxc bool -) - -const ( - usbcdcTxSizeMask uint8 = 0x3F - usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask - usbcdcTxBank1st uint8 = 0x00 - usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1 - usbcdcTxMaxRetriesAllowed uint8 = 5 -) - -// Flush flushes buffered data. -func (usbcdc *USBCDC) Flush() error { - if usbLineInfo.lineState > 0 { - idx := usbcdc.TxIdx.Get() - sz := idx & usbcdcTxSizeMask - bk := idx & usbcdcTxBankMask - if 0 < sz { - - if usbcdc.waitTxc { - // waiting for the next flush(), because the transmission is not complete - usbcdc.waitTxcRetryCount++ - return nil - } - usbcdc.waitTxc = true - usbcdc.waitTxcRetryCount = 0 - - // set the data - usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk])))) - if bk == usbcdcTxBank1st { - usbcdc.TxIdx.Set(usbcdcTxBank2nd) - } else { - usbcdc.TxIdx.Set(usbcdcTxBank1st) - } - - // clean multi packet size of bytes already sent - usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) - - // set count of bytes to be sent - usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE.SetBits((uint32(sz) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - - // clear transfer complete flag - setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) - - // send data by setting bank ready - setEPSTATUSSET(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY) - usbcdc.sent = true - } - } - return nil -} - -// 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 { - ok := false - for { - mask := interrupt.Disable() - - idx := usbcdc.TxIdx.Get() - if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask { - udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c - usbcdc.TxIdx.Set(idx + 1) - ok = true - } - - interrupt.Restore(mask) - - if ok { - break - } else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount { - mask := interrupt.Disable() - usbcdc.waitTxc = false - usbcdc.waitTxcRetryCount = 0 - usbcdc.TxIdx.Set(0) - usbLineInfo.lineState = 0 - interrupt.Restore(mask) - break - } else { - mask := interrupt.Disable() - if usbcdc.sent { - if usbcdc.waitTxc { - if (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) != 0 { - setEPSTATUSCLR(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY) - setEPINTFLAG(usb_CDC_ENDPOINT_IN, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) - usbcdc.waitTxc = false - usbcdc.Flush() - } - } else { - usbcdc.Flush() - } - } - interrupt.Restore(mask) - } - } - } - - 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 -} - -const ( - // these are SAMD51 specific. - usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0 - usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF - - usb_DEVICE_PCKSIZE_SIZE_Pos = 28 - usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7 - - usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14 - usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF -) - -var ( - usbEndpointDescriptors [8]usbDeviceDescriptor - - udd_ep_in_cache_buffer [7][128]uint8 - udd_ep_out_cache_buffer [7][128]uint8 - - 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} -) - -// Configure the USB CDC interface. The config is here for compatibility with the UART interface. -func (usbcdc *USBCDC) Configure(config UARTConfig) { - // reset USB interface - sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST) - for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) || - sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) { - } - - sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors)))) - - // configure pins - USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom}) - USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom}) - - // performs pad calibration from store fuses - handlePadCalibration() - - // run in standby - sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY) - - // set full speed - sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos) - - // attach - sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) - - // enable interrupt for end of reset - sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST) - - // enable interrupt for start of frame - sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF) - - // enable USB - 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() - - usbcdc.configured = true -} - -// Configured returns whether usbcdc is configured or not. -func (usbcdc *USBCDC) Configured() bool { - return usbcdc.configured -} - -func handlePadCalibration() { - // Load Pad Calibration data from non-volatile memory - // This requires registers that are not included in the SVD file. - // Modeled after defines from samd21g18a.h and nvmctrl.h: - // - // #define NVMCTRL_OTP4 0x00806020 - // - // #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4) - // #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */ - // #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos) - // #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos))) - - // #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4) - // #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */ - // #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos) - // #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos))) - - // #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4) - // #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */ - // #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos) - // #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos))) - // - fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4)) - calibTransN := uint16(fuse>>13) & uint16(0x1f) - calibTransP := uint16(fuse>>18) & uint16(0x1f) - calibTrim := uint16(fuse>>23) & uint16(0x7) - - if calibTransN == 0x1f { - calibTransN = 5 - } - sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos) - - if calibTransP == 0x1f { - calibTransP = 29 - } - sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos) - - if calibTrim == 0x7 { - calibTrim = 3 - } - sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos) -} - -func handleUSBIRQ(interrupt.Interrupt) { - // reset all interrupt flags - flags := sam.USB_DEVICE.INTFLAG.Get() - sam.USB_DEVICE.INTFLAG.Set(flags) - - // End of reset - if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 { - // Configure control endpoint - initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL) - - // Enable Setup-Received interrupt - setEPINTENSET(0, sam.USB_DEVICE_ENDPOINT_EPINTENSET_RXSTP) - - usbConfiguration = 0 - - // ack the End-Of-Reset interrupt - sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST) - } - - // Start of frame - if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 { - USB.Flush() - if hidCallback != nil && !waitHidTxc { - hidCallback() - } - // if you want to blink LED showing traffic, this would be the place... - } - - // Endpoint 0 Setup interrupt - if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP > 0 { - // ack setup received - setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP) - - // parse setup - setup := newUSBSetup(udd_ep_out_cache_buffer[0][:]) - - // Clear the Bank 0 ready flag on Control OUT - setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY) - usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - - ok := false - if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD { - // Standard Requests - ok = handleStandardSetup(setup) - } else { - // Class Interface Requests - if setup.wIndex == usb_CDC_ACM_INTERFACE { - ok = cdcSetup(setup) - } else if setup.bmRequestType == usb_SET_REPORT_TYPE && setup.bRequest == usb_SET_IDLE { - sendZlp() - ok = true - } - } - - if ok { - // set Bank1 ready - setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY) - } else { - // Stall endpoint - setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1) - } - - if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 { - // ack the stall - setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1) - - // clear stall request - setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1) - } - } - - // Now the actual transfer handlers, ignore endpoint number 0 (setup) - var i uint32 - for i = 1; i < uint32(len(endPoints)); i++ { - // Check if endpoint has a pending interrupt - epFlags := getEPINTFLAG(i) - if (epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 || - (epFlags&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 { - switch i { - case usb_CDC_ENDPOINT_OUT: - handleEndpoint(i) - setEPINTFLAG(i, epFlags) - case usb_CDC_ENDPOINT_IN, usb_CDC_ENDPOINT_ACM: - setEPSTATUSCLR(i, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY) - setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) - - if i == usb_CDC_ENDPOINT_IN { - USB.waitTxc = false - } - case usb_HID_ENDPOINT_IN: - setEPINTFLAG(i, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) - waitHidTxc = false - } - } - } -} - -func initEndpoint(ep, config uint32) { - switch config { - case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn: - // set packet size - usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) - - // set data buffer address - usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) - - // set endpoint type - setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos)) - - case usb_ENDPOINT_TYPE_BULK | usbEndpointOut: - // set packet size - usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) - - // set data buffer address - usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep])))) - - // set endpoint type - setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos)) - - // receive interrupts when current transfer complete - setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0) - - // set byte count to zero, we have not received anything yet - usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - - // ready for next transfer - setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY) - - case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut: - // TODO: not really anything, seems like... - - case usb_ENDPOINT_TYPE_BULK | usbEndpointIn: - // set packet size - usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) - - // set data buffer address - usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) - - // set endpoint type - setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos)) - - // NAK on endpoint IN, the bank is not yet filled in. - setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY) - - case usb_ENDPOINT_TYPE_CONTROL: - // Control OUT - // set packet size - usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) - - // set data buffer address - usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep])))) - - // set endpoint type - setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<> 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[:], setup.wLength) - return true - } - } - - if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { - if setup.bRequest == usb_CDC_SET_LINE_CODING { - b, err := receiveUSBControlPacket() - if err != nil { - return false - } - - 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 { - ResetProcessor() - } else { - // TODO: cancel any reset - } - sendZlp() - } - - if setup.bRequest == usb_CDC_SEND_BREAK { - // TODO: something with this value? - // breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL; - // return false; - sendZlp() - } - return true - } - return false -} - -// SendUSBHIDPacket sends a packet for USBHID (interrupt / in). -func SendUSBHIDPacket(ep uint32, data []byte) bool { - if waitHidTxc { - return false - } - - sendUSBPacket(ep, data, 0) - - // clear transfer complete flag - setEPINTFLAG(ep, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) - - // send data by setting bank ready - setEPSTATUSSET(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY) - - waitHidTxc = true - - return true -} - -//go:noinline -func sendUSBPacket(ep uint32, data []byte, maxsize uint16) { - l := uint16(len(data)) - if 0 < maxsize && maxsize < l { - l = maxsize - } - copy(udd_ep_in_cache_buffer[ep][:], data[:l]) - - // Set endpoint address for sending data - usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) - - // clear multi-packet size which is total bytes already sent - usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) - - // set byte count, which is total number of bytes to be sent - usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits((uint32(l) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) -} - -func receiveUSBControlPacket() ([cdcLineInfoSize]byte, error) { - var b [cdcLineInfoSize]byte - - // address - usbEndpointDescriptors[0].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0])))) - - // set byte count to zero - usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - - // set ready for next data - setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY) - - // Wait until OUT transfer is ready. - timeout := 300000 - for (getEPSTATUS(0) & sam.USB_DEVICE_ENDPOINT_EPSTATUS_BK0RDY) == 0 { - timeout-- - if timeout == 0 { - return b, errUSBCDCReadTimeout - } - } - - // Wait until OUT transfer is completed. - timeout = 300000 - for (getEPINTFLAG(0) & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) == 0 { - timeout-- - if timeout == 0 { - return b, errUSBCDCReadTimeout - } - } - - // get data - bytesread := uint32((usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.Get() >> - usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) - - if bytesread != cdcLineInfoSize { - return b, errUSBCDCBytesRead - } - - copy(b[:7], udd_ep_out_cache_buffer[0][:7]) - - return b, nil -} - -func handleEndpoint(ep uint32) { - // get data - count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >> - usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) - - // move to ring buffer - for i := 0; i < count; i++ { - USB.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF))) - } - - // set byte count to zero - usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) - - // set multi packet size to 64 - usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) - - // set ready for next data - setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY) -} - -func sendZlp() { - usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) -} - -func epPacketSize(size uint16) uint32 { - switch size { - case 8: - return 0 - case 16: - return 1 - case 32: - return 2 - case 64: - return 3 - case 128: - return 4 - case 256: - return 5 - case 512: - return 6 - case 1023: - return 7 - default: - return 0 - } -} - -func getEPCFG(ep uint32) uint8 { - return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Get() -} - -func setEPCFG(ep uint32, val uint8) { - sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Set(val) -} - -func setEPSTATUSCLR(ep uint32, val uint8) { - sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSCLR.Set(val) -} - -func setEPSTATUSSET(ep uint32, val uint8) { - sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSSET.Set(val) -} - -func getEPSTATUS(ep uint32) uint8 { - return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUS.Get() -} - -func getEPINTFLAG(ep uint32) uint8 { - return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Get() -} - -func setEPINTFLAG(ep uint32, val uint8) { - sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(val) -} - -func setEPINTENCLR(ep uint32, val uint8) { - sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENCLR.Set(val) -} - -func setEPINTENSET(ep uint32, val uint8) { - sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val) -} - // ResetProcessor should perform a system reset in preparation // to switch to the bootloader to flash new firmware. func ResetProcessor() { diff --git a/src/machine/machine_atsamd51_usb.go b/src/machine/machine_atsamd51_usb.go new file mode 100644 index 000000000..3cb53aae5 --- /dev/null +++ b/src/machine/machine_atsamd51_usb.go @@ -0,0 +1,460 @@ +//go:build (sam && atsamd51) || (sam && atsame5x) +// +build sam,atsamd51 sam,atsame5x + +package machine + +import ( + "device/sam" + "runtime/interrupt" + "unsafe" +) + +const ( + // these are SAMD51 specific. + usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos = 0 + usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask = 0x3FFF + + usb_DEVICE_PCKSIZE_SIZE_Pos = 28 + usb_DEVICE_PCKSIZE_SIZE_Mask = 0x7 + + usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos = 14 + usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Mask = 0x3FFF +) + +// Configure the USB peripheral. The config is here for compatibility with the UART interface. +func (dev *USBDevice) Configure(config UARTConfig) { + // reset USB interface + sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_SWRST) + for sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_SWRST) || + sam.USB_DEVICE.SYNCBUSY.HasBits(sam.USB_DEVICE_SYNCBUSY_ENABLE) { + } + + sam.USB_DEVICE.DESCADD.Set(uint32(uintptr(unsafe.Pointer(&usbEndpointDescriptors)))) + + // configure pins + USBCDC_DM_PIN.Configure(PinConfig{Mode: PinCom}) + USBCDC_DP_PIN.Configure(PinConfig{Mode: PinCom}) + + // performs pad calibration from store fuses + handlePadCalibration() + + // run in standby + sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_RUNSTDBY) + + // set full speed + sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos) + + // attach + sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) + + // enable interrupt for end of reset + sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST) + + // enable interrupt for start of frame + sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_SOF) + + // enable USB + sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE) + + // enable IRQ + 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() +} + +func handlePadCalibration() { + // Load Pad Calibration data from non-volatile memory + // This requires registers that are not included in the SVD file. + // Modeled after defines from samd21g18a.h and nvmctrl.h: + // + // #define NVMCTRL_OTP4 0x00806020 + // + // #define USB_FUSES_TRANSN_ADDR (NVMCTRL_OTP4 + 4) + // #define USB_FUSES_TRANSN_Pos 13 /**< \brief (NVMCTRL_OTP4) USB pad Transn calibration */ + // #define USB_FUSES_TRANSN_Msk (0x1Fu << USB_FUSES_TRANSN_Pos) + // #define USB_FUSES_TRANSN(value) ((USB_FUSES_TRANSN_Msk & ((value) << USB_FUSES_TRANSN_Pos))) + + // #define USB_FUSES_TRANSP_ADDR (NVMCTRL_OTP4 + 4) + // #define USB_FUSES_TRANSP_Pos 18 /**< \brief (NVMCTRL_OTP4) USB pad Transp calibration */ + // #define USB_FUSES_TRANSP_Msk (0x1Fu << USB_FUSES_TRANSP_Pos) + // #define USB_FUSES_TRANSP(value) ((USB_FUSES_TRANSP_Msk & ((value) << USB_FUSES_TRANSP_Pos))) + + // #define USB_FUSES_TRIM_ADDR (NVMCTRL_OTP4 + 4) + // #define USB_FUSES_TRIM_Pos 23 /**< \brief (NVMCTRL_OTP4) USB pad Trim calibration */ + // #define USB_FUSES_TRIM_Msk (0x7u << USB_FUSES_TRIM_Pos) + // #define USB_FUSES_TRIM(value) ((USB_FUSES_TRIM_Msk & ((value) << USB_FUSES_TRIM_Pos))) + // + fuse := *(*uint32)(unsafe.Pointer(uintptr(0x00806020) + 4)) + calibTransN := uint16(fuse>>13) & uint16(0x1f) + calibTransP := uint16(fuse>>18) & uint16(0x1f) + calibTrim := uint16(fuse>>23) & uint16(0x7) + + if calibTransN == 0x1f { + calibTransN = 5 + } + sam.USB_DEVICE.PADCAL.SetBits(calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos) + + if calibTransP == 0x1f { + calibTransP = 29 + } + sam.USB_DEVICE.PADCAL.SetBits(calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos) + + if calibTrim == 0x7 { + calibTrim = 3 + } + sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos) +} + +func handleUSBIRQ(intr interrupt.Interrupt) { + // reset all interrupt flags + flags := sam.USB_DEVICE.INTFLAG.Get() + sam.USB_DEVICE.INTFLAG.Set(flags) + + // End of reset + if (flags & sam.USB_DEVICE_INTFLAG_EORST) > 0 { + // Configure control endpoint + initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL) + + usbConfiguration = 0 + + // ack the End-Of-Reset interrupt + sam.USB_DEVICE.INTFLAG.Set(sam.USB_DEVICE_INTFLAG_EORST) + } + + // Start of frame + if (flags & sam.USB_DEVICE_INTFLAG_SOF) > 0 { + // if you want to blink LED showing traffic, this would be the place... + } + + // Endpoint 0 Setup interrupt + if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP > 0 { + // ack setup received + setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_RXSTP) + + // parse setup + setup := newUSBSetup(udd_ep_out_cache_buffer[0][:]) + + // Clear the Bank 0 ready flag on Control OUT + setEPSTATUSCLR(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY) + usbEndpointDescriptors[0].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) + + ok := false + if (setup.BmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD { + // Standard Requests + ok = handleStandardSetup(setup) + } else { + // Class Interface Requests + if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil { + ok = callbackUSBSetup[setup.WIndex](setup) + } + } + + if ok { + // set Bank1 ready + setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPSTATUSSET_BK1RDY) + } else { + // Stall endpoint + setEPSTATUSSET(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1) + } + + if getEPINTFLAG(0)&sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1 > 0 { + // ack the stall + setEPINTFLAG(0, sam.USB_DEVICE_ENDPOINT_EPINTFLAG_STALL1) + + // clear stall request + setEPINTENCLR(0, sam.USB_DEVICE_ENDPOINT_EPINTENCLR_STALL1) + } + } + + // Now the actual transfer handlers, ignore endpoint number 0 (setup) + var i uint32 + for i = 1; i < uint32(len(endPoints)); i++ { + // Check if endpoint has a pending interrupt + epFlags := getEPINTFLAG(i) + setEPINTFLAG(i, epFlags) + if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT0) > 0 { + buf := handleEndpointRx(i) + if callbackUSBRx[i] != nil { + callbackUSBRx[i](buf) + } + } else if (epFlags & sam.USB_DEVICE_ENDPOINT_EPINTFLAG_TRCPT1) > 0 { + if callbackUSBTx[i] != nil { + callbackUSBTx[i]() + } + } + } +} + +func initEndpoint(ep, config uint32) { + switch config { + case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn: + // set packet size + usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) + + // set endpoint type + setEPCFG(ep, ((usb_ENDPOINT_TYPE_INTERRUPT + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos)) + + setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1) + + case usb_ENDPOINT_TYPE_BULK | usbEndpointOut: + // set packet size + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep])))) + + // set endpoint type + setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE0_Pos)) + + // receive interrupts when current transfer complete + setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT0) + + // set byte count to zero, we have not received anything yet + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) + + // ready for next transfer + setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY) + + case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut: + // TODO: not really anything, seems like... + + case usb_ENDPOINT_TYPE_BULK | usbEndpointIn: + // set packet size + usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep])))) + + // set endpoint type + setEPCFG(ep, ((usb_ENDPOINT_TYPE_BULK + 1) << sam.USB_DEVICE_ENDPOINT_EPCFG_EPTYPE1_Pos)) + + // NAK on endpoint IN, the bank is not yet filled in. + setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK1RDY) + + setEPINTENSET(ep, sam.USB_DEVICE_ENDPOINT_EPINTENSET_TRCPT1) + + case usb_ENDPOINT_TYPE_CONTROL: + // Control OUT + // set packet size + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep])))) + + // set endpoint type + setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<> + usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) + + if bytesread != cdcLineInfoSize { + return b, errUSBCDCBytesRead + } + + copy(b[:7], udd_ep_out_cache_buffer[0][:7]) + + return b, nil +} + +func handleEndpointRx(ep uint32) []byte { + // get data + count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.Get() >> + usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) + + // move to ring buffer + buf := make([]byte, count) + copy(buf, udd_ep_out_cache_buffer[ep][:]) + + // set byte count to zero + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) + + // set multi packet size to 64 + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE.SetBits(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) + + // set ready for next data + setEPSTATUSCLR(ep, sam.USB_DEVICE_ENDPOINT_EPSTATUSCLR_BK0RDY) + + return buf[:count] +} + +func SendZlp() { + usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE.ClearBits(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) +} + +func epPacketSize(size uint16) uint32 { + switch size { + case 8: + return 0 + case 16: + return 1 + case 32: + return 2 + case 64: + return 3 + case 128: + return 4 + case 256: + return 5 + case 512: + return 6 + case 1023: + return 7 + default: + return 0 + } +} + +func getEPCFG(ep uint32) uint8 { + return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Get() +} + +func setEPCFG(ep uint32, val uint8) { + sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPCFG.Set(val) +} + +func setEPSTATUSCLR(ep uint32, val uint8) { + sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSCLR.Set(val) +} + +func setEPSTATUSSET(ep uint32, val uint8) { + sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUSSET.Set(val) +} + +func getEPSTATUS(ep uint32) uint8 { + return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPSTATUS.Get() +} + +func getEPINTFLAG(ep uint32) uint8 { + return sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Get() +} + +func setEPINTFLAG(ep uint32, val uint8) { + sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTFLAG.Set(val) +} + +func setEPINTENCLR(ep uint32, val uint8) { + sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENCLR.Set(val) +} + +func setEPINTENSET(ep uint32, val uint8) { + sam.USB_DEVICE.DEVICE_ENDPOINT[ep].EPINTENSET.Set(val) +} diff --git a/src/machine/machine_nrf52840_usb.go b/src/machine/machine_nrf52840_usb.go index 7260ac15f..5ed562ffe 100644 --- a/src/machine/machine_nrf52840_usb.go +++ b/src/machine/machine_nrf52840_usb.go @@ -11,143 +11,15 @@ import ( "unsafe" ) -// USBCDC is the USB CDC aka serial over USB interface on the nRF52840 -type USBCDC struct { - Buffer *RingBuffer - interrupt interrupt.Interrupt - initcomplete bool - TxIdx volatile.Register8 - waitTxc bool - waitTxcRetryCount uint8 - sent bool -} - -const ( - usbcdcTxSizeMask uint8 = 0x3F - usbcdcTxBankMask uint8 = ^usbcdcTxSizeMask - usbcdcTxBank1st uint8 = 0x00 - usbcdcTxBank2nd uint8 = usbcdcTxSizeMask + 1 - usbcdcTxMaxRetriesAllowed uint8 = 5 -) - -// Flush flushes buffered data. -func (usbcdc *USBCDC) Flush() error { - if usbLineInfo.lineState > 0 { - idx := usbcdc.TxIdx.Get() - sz := idx & usbcdcTxSizeMask - bk := idx & usbcdcTxBankMask - if 0 < sz { - - if usbcdc.waitTxc { - // waiting for the next flush(), because the transmission is not complete - usbcdc.waitTxcRetryCount++ - return nil - } - usbcdc.waitTxc = true - usbcdc.waitTxcRetryCount = 0 - - // set the data - enterCriticalSection() - sendViaEPIn( - usb_CDC_ENDPOINT_IN, - &udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][bk], - int(sz), - ) - if bk == usbcdcTxBank1st { - usbcdc.TxIdx.Set(usbcdcTxBank2nd) - } else { - usbcdc.TxIdx.Set(usbcdcTxBank1st) - } - - usbcdc.sent = true - } - } - return nil -} - -// 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 { - ok := false - for { - mask := interrupt.Disable() - - idx := usbcdc.TxIdx.Get() - if (idx & usbcdcTxSizeMask) < usbcdcTxSizeMask { - udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][idx] = c - usbcdc.TxIdx.Set(idx + 1) - ok = true - } - - interrupt.Restore(mask) - - if ok { - break - } else if usbcdcTxMaxRetriesAllowed < usbcdc.waitTxcRetryCount { - mask := interrupt.Disable() - usbcdc.waitTxc = false - usbcdc.waitTxcRetryCount = 0 - usbcdc.TxIdx.Set(0) - usbLineInfo.lineState = 0 - interrupt.Restore(mask) - break - } else { - mask := interrupt.Disable() - if usbcdc.sent { - if usbcdc.waitTxc { - if !easyDMABusy.HasBits(1) { - usbcdc.waitTxc = false - usbcdc.Flush() - } - } else { - usbcdc.Flush() - } - } - interrupt.Restore(mask) - } - } - } - - 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 = &_USB - _USB = USBCDC{Buffer: NewRingBuffer()} - waitHidTxc bool - - 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 + ptr *byte + count int + offset 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 + epinen uint32 + epouten uint32 + easyDMABusy volatile.Register8 ) // enterCriticalSection is used to protect access to easyDMA - only one thing @@ -167,19 +39,15 @@ 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) { - if usbcdc.initcomplete { - return - } - +// Configure the USB peripheral. The config is here for compatibility with the UART interface. +func (dev *USBDevice) 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() + intr := interrupt.New(nrf.IRQ_USBD, handleUSBIRQ) + intr.SetPriority(0x40) // interrupt priority 2 (lower number means more important) + intr.Enable() // enable USB nrf.USBD.ENABLE.Set(1) @@ -194,17 +62,12 @@ func (usbcdc *USBCDC) Configure(config UARTConfig) { ) nrf.USBD.USBPULLUP.Set(0) - - usbcdc.initcomplete = true } -func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { +func handleUSBIRQ(intr interrupt.Interrupt) { if nrf.USBD.EVENTS_SOF.Get() == 1 { nrf.USBD.EVENTS_SOF.Set(0) - usbcdc.Flush() - if hidCallback != nil && !waitHidTxc { - hidCallback() - } + // if you want to blink LED showing traffic, this would be the place... } @@ -228,25 +91,30 @@ 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 + ptr := sendOnEP0DATADONE.ptr + count := sendOnEP0DATADONE.count + if count > usbEndpointPacketSize { + sendOnEP0DATADONE.offset += usbEndpointPacketSize + sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[0][sendOnEP0DATADONE.offset] + count = usbEndpointPacketSize + } + sendOnEP0DATADONE.count -= count sendViaEPIn( 0, - sendOnEP0DATADONE.ptr, - sendOnEP0DATADONE.count, + ptr, + count, ) // clear, so we know we're done - sendOnEP0DATADONE.ptr = nil + if sendOnEP0DATADONE.count == 0 { + sendOnEP0DATADONE.ptr = nil + sendOnEP0DATADONE.offset = 0 + } } else { // no more data, so set status stage - nrf.USBD.TASKS_EP0STATUS.Set(1) + SendZlp() // nrf.USBD.TASKS_EP0STATUS.Set(1) } return } @@ -260,15 +128,13 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { setup := parseUSBSetupRegisters() ok := false - if (setup.bmRequestType & usb_REQUEST_TYPE) == usb_REQUEST_STANDARD { + 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) - } else if setup.bmRequestType == usb_SET_REPORT_TYPE && setup.bRequest == usb_SET_IDLE { - sendZlp() - ok = true + // Class Interface Requests + if setup.WIndex < uint16(len(callbackUSBSetup)) && callbackUSBSetup[setup.WIndex] != nil { + ok = callbackUSBSetup[setup.WIndex](setup) } } @@ -288,25 +154,16 @@ func (usbcdc *USBCDC) handleInterrupt(interrupt.Interrupt) { // 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 { - usbcdc.waitTxc = false - exitCriticalSection() - } - case usb_HID_ENDPOINT_IN: - waitHidTxc = false + if inDataDone { + if callbackUSBTx[i] != nil { + callbackUSBTx[i]() } + } else 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) } } } @@ -315,38 +172,23 @@ 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)) + buf := handleEndpointRx(uint32(i)) + if callbackUSBRx[i] != nil { + callbackUSBRx[i](buf) } 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 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)), } } @@ -372,143 +214,17 @@ func initEndpoint(ep, config uint32) { enableEPIn(0) enableEPOut(0) nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0) - nrf.USBD.TASKS_EP0STATUS.Set(1) + SendZlp() // 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, setup.wLength) - 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, setup.wLength) - 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]) - } - - // Enable interrupt for HID messages from host - if hidCallback != nil { - nrf.USBD.INTENSET.Set(nrf.USBD_INTENSET_ENDEPOUT0 << usb_HID_ENDPOINT_IN) - } - - usbConfiguration = setup.wValueL - return true - } else { - return false - } - - case usb_GET_INTERFACE: - buff := []byte{usbSetInterface} - sendUSBPacket(0, buff, setup.wLength) - 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 { - var b [cdcLineInfoSize]byte - 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[:], setup.wLength) - 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 -} - -// SendUSBHIDPacket sends a packet for USBHID (interrupt / in). -func SendUSBHIDPacket(ep uint32, data []byte) bool { - if waitHidTxc { - return false - } - +// SendUSBInPacket sends a packet for USBHID (interrupt in / bulk in). +func SendUSBInPacket(ep uint32, data []byte) bool { sendUSBPacket(ep, data, 0) // clear transfer complete flag nrf.USBD.INTENCLR.Set(nrf.USBD_INTENCLR_ENDEPOUT0 << 4) - waitHidTxc = true - return true } @@ -520,7 +236,8 @@ func sendUSBPacket(ep uint32, data []byte, maxsize uint16) { } copy(udd_ep_in_cache_buffer[ep][:], data[:count]) if ep == 0 && count > usbEndpointPacketSize { - sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[ep][usbEndpointPacketSize] + sendOnEP0DATADONE.offset = usbEndpointPacketSize + sendOnEP0DATADONE.ptr = &udd_ep_in_cache_buffer[ep][sendOnEP0DATADONE.offset] sendOnEP0DATADONE.count = count - usbEndpointPacketSize count = usbEndpointPacketSize } @@ -531,20 +248,21 @@ func sendUSBPacket(ep uint32, data []byte, maxsize uint16) { ) } -func (usbcdc *USBCDC) handleEndpoint(ep uint32) { +func handleEndpointRx(ep uint32) []byte { // 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])) - } + buf := make([]byte, count) + copy(buf, udd_ep_out_cache_buffer[ep][:]) // set ready for next data nrf.USBD.SIZE.EPOUT[ep].Set(0) + + return buf[:count] } -func sendZlp() { +func SendZlp() { nrf.USBD.TASKS_EP0STATUS.Set(1) } @@ -565,3 +283,52 @@ func enableEPIn(ep uint32) { epinen = epinen | (nrf.USBD_EPINEN_IN0 << ep) nrf.USBD.EPINEN.Set(epinen) } + +func handleUSBSetAddress(setup USBSetup) bool { + // nrf USBD handles this + return true +} + +func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) { + var b [cdcLineInfoSize]byte + + nrf.USBD.TASKS_EP0RCVOUT.Set(1) + + nrf.USBD.EPOUT[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[0])))) + nrf.USBD.EPOUT[0].MAXCNT.Set(64) + + timeout := 300000 + count := 0 + for { + if nrf.USBD.EVENTS_EP0DATADONE.Get() == 1 { + nrf.USBD.EVENTS_EP0DATADONE.Set(0) + count = int(nrf.USBD.SIZE.EPOUT[0].Get()) + nrf.USBD.TASKS_STARTEPOUT[0].Set(1) + break + } + timeout-- + if timeout == 0 { + return b, errUSBCDCReadTimeout + } + } + + timeout = 300000 + for { + if nrf.USBD.EVENTS_ENDEPOUT[0].Get() == 1 { + nrf.USBD.EVENTS_ENDEPOUT[0].Set(0) + break + } + + timeout-- + if timeout == 0 { + return b, errUSBCDCReadTimeout + } + } + + nrf.USBD.TASKS_EP0STATUS.Set(1) + nrf.USBD.TASKS_EP0RCVOUT.Set(0) + + copy(b[:7], udd_ep_out_cache_buffer[0][:count]) + + return b, nil +} diff --git a/src/machine/machine_nrf52840_usb_reset_uf2.go b/src/machine/machine_nrf52840_usb_reset_uf2.go index ec685529e..f9187c0a4 100644 --- a/src/machine/machine_nrf52840_usb_reset_uf2.go +++ b/src/machine/machine_nrf52840_usb_reset_uf2.go @@ -14,12 +14,10 @@ const ( DFU_MAGIC_OTA_RESET = 0xA8 ) -// checkShouldReset is called by the USB-CDC implementation to check whether to -// reset into the bootloader/OTA and if so, resets the chip appropriately. -func checkShouldReset() { - if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 { - EnterUF2Bootloader() - } +// ResetProcessor should perform a system reset in preparation +// to switch to the bootloader to flash new firmware. +func ResetProcessor() { + EnterUF2Bootloader() } // EnterSerialBootloader resets the chip into the serial bootloader. After diff --git a/src/machine/serial-usb.go b/src/machine/serial-usb.go index ac484f87a..7216baed8 100644 --- a/src/machine/serial-usb.go +++ b/src/machine/serial-usb.go @@ -3,5 +3,17 @@ package machine +type Serialer interface { + WriteByte(c byte) error + Write(data []byte) (n int, err error) + Configure(config UARTConfig) + Configured() bool + Buffered() int + ReadByte() (byte, error) +} + +//go:linkname NewUSBCDC machine/usb/cdc.New +func NewUSBCDC() Serialer + // Serial is implemented via USB (USB-CDC). -var Serial = USB +var Serial = NewUSBCDC() diff --git a/src/machine/usb.go b/src/machine/usb.go index 354bc1b14..938123d4f 100644 --- a/src/machine/usb.go +++ b/src/machine/usb.go @@ -8,24 +8,14 @@ import ( "runtime/volatile" ) -var usbDescriptor = descriptorCDC +type USBDevice struct { +} var ( - errUSBCDCBufferEmpty = errors.New("USB-CDC buffer empty") - errUSBCDCWriteByteTimeout = errors.New("USB-CDC write byte timeout") - errUSBCDCReadTimeout = errors.New("USB-CDC read timeout") - errUSBCDCBytesRead = errors.New("USB-CDC invalid number of bytes read") + USB = &USBDevice{} ) -const cdcLineInfoSize = 7 - -type cdcLineInfo struct { - dwDTERate uint32 - bCharFormat uint8 - bParityType uint8 - bDataBits uint8 - lineState uint8 -} +var usbDescriptor = descriptorCDC // strToUTF16LEDescriptor converts a utf8 string into a string descriptor // note: the following code only converts ascii characters to UTF16LE. In order @@ -47,11 +37,34 @@ var ( usb_STRING_LANGUAGE = [2]uint16{(3 << 8) | (2 + 2), 0x0409} // English ) +const cdcLineInfoSize = 7 + +var ( + errUSBCDCBufferEmpty = errors.New("USB-CDC buffer empty") + errUSBCDCWriteByteTimeout = errors.New("USB-CDC write byte timeout") + errUSBCDCReadTimeout = errors.New("USB-CDC read timeout") + errUSBCDCBytesRead = errors.New("USB-CDC invalid number of bytes read") +) + +var ( + usbEndpointDescriptors [8]usbDeviceDescriptor + + udd_ep_in_cache_buffer [7][128 * 2]uint8 + udd_ep_out_cache_buffer [7][128 * 2]uint8 + + isEndpointHalt = false + isRemoteWakeUpEnabled = false + + usbConfiguration uint8 + usbSetInterface uint8 +) + const ( usb_IMANUFACTURER = 1 usb_IPRODUCT = 2 usb_ISERIAL = 3 + usb_ENDPOINT_TYPE_DISABLE = 0xFF usb_ENDPOINT_TYPE_CONTROL = 0x00 usb_ENDPOINT_TYPE_ISOCHRONOUS = 0x01 usb_ENDPOINT_TYPE_BULK = 0x02 @@ -102,12 +115,16 @@ const ( usb_CDC_ACM_INTERFACE = 0 // CDC ACM usb_CDC_DATA_INTERFACE = 1 // CDC Data usb_CDC_FIRST_ENDPOINT = 1 + usb_HID_INTERFACE = 2 // HID // Endpoint - usb_CDC_ENDPOINT_ACM = 1 - usb_CDC_ENDPOINT_OUT = 2 - usb_CDC_ENDPOINT_IN = 3 - usb_HID_ENDPOINT_IN = 4 + usb_CONTROL_ENDPOINT = 0 + usb_CDC_ENDPOINT_ACM = 1 + usb_CDC_ENDPOINT_OUT = 2 + usb_CDC_ENDPOINT_IN = 3 + usb_HID_ENDPOINT_IN = 4 + usb_MIDI_ENDPOINT_OUT = 5 + usb_MIDI_ENDPOINT_IN = 6 // bmRequestType usb_REQUEST_HOSTTODEVICE = 0x00 @@ -128,27 +145,23 @@ const ( usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE) usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE = (usb_REQUEST_HOSTTODEVICE | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE) usb_REQUEST_DEVICETOHOST_STANDARD_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_STANDARD | usb_REQUEST_INTERFACE) +) - // CDC Class requests - usb_CDC_SET_LINE_CODING = 0x20 - usb_CDC_GET_LINE_CODING = 0x21 - usb_CDC_SET_CONTROL_LINE_STATE = 0x22 - usb_CDC_SEND_BREAK = 0x23 +var ( + waitTxc [8]bool + callbackUSBTx [8]func() + callbackUSBRx [8]func([]byte) + callbackUSBSetup [3]func(USBSetup) bool - usb_CDC_V1_10 = 0x0110 - usb_CDC_COMMUNICATION_INTERFACE_CLASS = 0x02 - - usb_CDC_CALL_MANAGEMENT = 0x01 - usb_CDC_ABSTRACT_CONTROL_MODEL = 0x02 - usb_CDC_HEADER = 0x00 - usb_CDC_ABSTRACT_CONTROL_MANAGEMENT = 0x02 - usb_CDC_UNION = 0x06 - usb_CDC_CS_INTERFACE = 0x24 - usb_CDC_CS_ENDPOINT = 0x25 - usb_CDC_DATA_INTERFACE_CLASS = 0x0A - - usb_CDC_LINESTATE_DTR = 0x01 - usb_CDC_LINESTATE_RTS = 0x02 + endPoints = []uint32{ + usb_CONTROL_ENDPOINT: usb_ENDPOINT_TYPE_CONTROL, + usb_CDC_ENDPOINT_ACM: (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn), + usb_CDC_ENDPOINT_OUT: (usb_ENDPOINT_TYPE_BULK | usbEndpointOut), + usb_CDC_ENDPOINT_IN: (usb_ENDPOINT_TYPE_BULK | usbEndpointIn), + usb_HID_ENDPOINT_IN: (usb_ENDPOINT_TYPE_DISABLE), // Interrupt In + usb_MIDI_ENDPOINT_OUT: (usb_ENDPOINT_TYPE_DISABLE), // Bulk Out + usb_MIDI_ENDPOINT_IN: (usb_ENDPOINT_TYPE_DISABLE), // Bulk In + } ) // usbDeviceDescBank is the USB device endpoint descriptor. @@ -186,130 +199,63 @@ type usbDeviceDescriptor struct { // uint16_t wIndex; // uint16_t wLength; // } USBSetup; -type usbSetup struct { - bmRequestType uint8 - bRequest uint8 - wValueL uint8 - wValueH uint8 - wIndex uint16 - wLength uint16 +type USBSetup struct { + BmRequestType uint8 + BRequest uint8 + WValueL uint8 + WValueH uint8 + WIndex uint16 + WLength uint16 } -func newUSBSetup(data []byte) usbSetup { - u := usbSetup{} - u.bmRequestType = uint8(data[0]) - u.bRequest = uint8(data[1]) - u.wValueL = uint8(data[2]) - u.wValueH = uint8(data[3]) - u.wIndex = uint16(data[4]) | (uint16(data[5]) << 8) - u.wLength = uint16(data[6]) | (uint16(data[7]) << 8) +func newUSBSetup(data []byte) USBSetup { + u := USBSetup{} + u.BmRequestType = uint8(data[0]) + u.BRequest = uint8(data[1]) + u.WValueL = uint8(data[2]) + u.WValueH = uint8(data[3]) + u.WIndex = uint16(data[4]) | (uint16(data[5]) << 8) + u.WLength = uint16(data[6]) | (uint16(data[7]) << 8) return u } -// USBCDC is the serial interface that works over the USB port. -// To implement the USBCDC interface for a board, you must declare a concrete type as follows: -// -// type USBCDC struct { -// Buffer *RingBuffer -// } -// -// You can also add additional members to this struct depending on your implementation, -// but the *RingBuffer is required. -// When you are declaring the USBCDC for your board, make sure that you also declare the -// RingBuffer using the NewRingBuffer() function: -// -// USBCDC{Buffer: NewRingBuffer()} -// - -// Read from the RX buffer. -func (usbcdc *USBCDC) Read(data []byte) (n int, err error) { - // check if RX buffer is empty - size := usbcdc.Buffered() - if size == 0 { - return 0, nil - } - - // Make sure we do not read more from buffer than the data slice can hold. - if len(data) < size { - size = len(data) - } - - // only read number of bytes used from buffer - for i := 0; i < size; i++ { - v, _ := usbcdc.ReadByte() - data[i] = v - } - - return size, nil -} - -// Write data to the USBCDC. -func (usbcdc *USBCDC) Write(data []byte) (n int, err error) { - for _, v := range data { - usbcdc.WriteByte(v) - } - return len(data), nil -} - -// ReadByte reads a single byte from the RX buffer. -// If there is no data in the buffer, returns an error. -func (usbcdc *USBCDC) ReadByte() (byte, error) { - // check if RX buffer is empty - buf, ok := usbcdc.Buffer.Get() - if !ok { - return 0, errUSBCDCBufferEmpty - } - return buf, nil -} - -// Buffered returns the number of bytes currently stored in the RX buffer. -func (usbcdc *USBCDC) Buffered() int { - return int(usbcdc.Buffer.Used()) -} - -// Receive handles adding data to the UART's data buffer. -// Usually called by the IRQ handler for a machine. -func (usbcdc *USBCDC) Receive(data byte) { - usbcdc.Buffer.Put(data) -} - // sendDescriptor creates and sends the various USB descriptor types that // can be requested by the host. -func sendDescriptor(setup usbSetup) { - switch setup.wValueH { +func sendDescriptor(setup USBSetup) { + switch setup.WValueH { case usb_CONFIGURATION_DESCRIPTOR_TYPE: - sendUSBPacket(0, usbDescriptor.Configuration, setup.wLength) + sendUSBPacket(0, usbDescriptor.Configuration, setup.WLength) return case usb_DEVICE_DESCRIPTOR_TYPE: // composite descriptor usbDescriptor.Configure(usb_VID, usb_PID) - sendUSBPacket(0, usbDescriptor.Device, setup.wLength) + sendUSBPacket(0, usbDescriptor.Device, setup.WLength) return case usb_STRING_DESCRIPTOR_TYPE: - switch setup.wValueL { + switch setup.WValueL { case 0: b := []byte{0x04, 0x03, 0x09, 0x04} - sendUSBPacket(0, b, setup.wLength) + sendUSBPacket(0, b, setup.WLength) case usb_IPRODUCT: b := make([]byte, (len(usb_STRING_PRODUCT)<<1)+2) strToUTF16LEDescriptor(usb_STRING_PRODUCT, b) - sendUSBPacket(0, b, setup.wLength) + sendUSBPacket(0, b, setup.WLength) case usb_IMANUFACTURER: b := make([]byte, (len(usb_STRING_MANUFACTURER)<<1)+2) strToUTF16LEDescriptor(usb_STRING_MANUFACTURER, b) - sendUSBPacket(0, b, setup.wLength) + sendUSBPacket(0, b, setup.WLength) case usb_ISERIAL: // TODO: allow returning a product serial number - sendZlp() + SendZlp() } return case usb_HID_REPORT_TYPE: - if h, ok := usbDescriptor.HID[setup.wIndex]; ok { - sendUSBPacket(0, h, setup.wLength) + if h, ok := usbDescriptor.HID[setup.WIndex]; ok { + sendUSBPacket(0, h, setup.WLength) return } case usb_DEVICE_QUALIFIER: @@ -318,21 +264,112 @@ func sendDescriptor(setup usbSetup) { } // do not know how to handle this message, so return zero - sendZlp() + SendZlp() return } -// EnableHID enables HID. This function must be executed from the init(). -func EnableHID(callback func()) { - usbDescriptor = descriptorCDCHID - endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL, - (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn), - (usb_ENDPOINT_TYPE_BULK | usbEndpointOut), - (usb_ENDPOINT_TYPE_BULK | usbEndpointIn), - (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn)} +func handleStandardSetup(setup USBSetup) bool { + switch setup.BRequest { + case usb_GET_STATUS: + buf := []byte{0, 0} - hidCallback = callback + if setup.BmRequestType != 0 { // endpoint + // TODO: actually check if the endpoint in question is currently halted + if isEndpointHalt { + buf[0] = 1 + } + } + + sendUSBPacket(0, buf, setup.WLength) + return true + + case usb_CLEAR_FEATURE: + if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP + isRemoteWakeUpEnabled = false + } else if setup.WValueL == 0 { // ENDPOINTHALT + isEndpointHalt = false + } + SendZlp() + return true + + case usb_SET_FEATURE: + if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP + isRemoteWakeUpEnabled = true + } else if setup.WValueL == 0 { // ENDPOINTHALT + isEndpointHalt = true + } + SendZlp() + return true + + case usb_SET_ADDRESS: + return handleUSBSetAddress(setup) + + case usb_GET_DESCRIPTOR: + sendDescriptor(setup) + return true + + case usb_SET_DESCRIPTOR: + return false + + case usb_GET_CONFIGURATION: + buff := []byte{usbConfiguration} + sendUSBPacket(0, buff, setup.WLength) + 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 + + SendZlp() + return true + } else { + return false + } + + case usb_GET_INTERFACE: + buff := []byte{usbSetInterface} + sendUSBPacket(0, buff, setup.WLength) + return true + + case usb_SET_INTERFACE: + usbSetInterface = setup.WValueL + + SendZlp() + return true + + default: + return true + } } -// hidCallback is a variable that holds the callback when using HID. -var hidCallback func() +func EnableCDC(callback func(), callbackRx func([]byte), callbackSetup func(USBSetup) bool) { + //usbDescriptor = descriptorCDC + endPoints[usb_CDC_ENDPOINT_ACM] = (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn) + endPoints[usb_CDC_ENDPOINT_OUT] = (usb_ENDPOINT_TYPE_BULK | usbEndpointOut) + endPoints[usb_CDC_ENDPOINT_IN] = (usb_ENDPOINT_TYPE_BULK | usbEndpointIn) + callbackUSBRx[usb_CDC_ENDPOINT_OUT] = callbackRx + callbackUSBTx[usb_CDC_ENDPOINT_IN] = callback + callbackUSBSetup[usb_CDC_ACM_INTERFACE] = callbackSetup // 0x02 (Communications and CDC Control) + callbackUSBSetup[usb_CDC_DATA_INTERFACE] = nil // 0x0A (CDC-Data) +} + +// EnableHID enables HID. This function must be executed from the init(). +func EnableHID(callback func(), callbackRx func([]byte), callbackSetup func(USBSetup) bool) { + usbDescriptor = descriptorCDCHID + endPoints[usb_HID_ENDPOINT_IN] = (usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn) + callbackUSBTx[usb_HID_ENDPOINT_IN] = callback + callbackUSBSetup[usb_HID_INTERFACE] = callbackSetup // 0x03 (HID - Human Interface Device) +} + +// EnableMIDI enables MIDI. This function must be executed from the init(). +func EnableMIDI(callback func(), callbackRx func([]byte), callbackSetup func(USBSetup) bool) { + usbDescriptor = descriptorCDCMIDI + endPoints[usb_MIDI_ENDPOINT_OUT] = (usb_ENDPOINT_TYPE_BULK | usbEndpointOut) + endPoints[usb_MIDI_ENDPOINT_IN] = (usb_ENDPOINT_TYPE_BULK | usbEndpointIn) + callbackUSBRx[usb_MIDI_ENDPOINT_OUT] = callbackRx + callbackUSBTx[usb_MIDI_ENDPOINT_IN] = callback +} diff --git a/src/machine/usb/cdc/buffer.go b/src/machine/usb/cdc/buffer.go new file mode 100644 index 000000000..d881b0dbc --- /dev/null +++ b/src/machine/usb/cdc/buffer.go @@ -0,0 +1,118 @@ +package cdc + +import ( + "runtime/volatile" +) + +const bufferSize = 128 + +// RingBuffer is ring buffer implementation inspired by post at +// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php +type RingBuffer struct { + rxbuffer [bufferSize]volatile.Register8 + head volatile.Register8 + tail volatile.Register8 +} + +// NewRingBuffer returns a new ring buffer. +func NewRingBuffer() *RingBuffer { + return &RingBuffer{} +} + +// Used returns how many bytes in buffer have been used. +func (rb *RingBuffer) Used() uint8 { + return uint8(rb.head.Get() - rb.tail.Get()) +} + +// Put stores a byte in the buffer. If the buffer is already +// full, the method will return false. +func (rb *RingBuffer) Put(val byte) bool { + if rb.Used() != bufferSize { + rb.head.Set(rb.head.Get() + 1) + rb.rxbuffer[rb.head.Get()%bufferSize].Set(val) + return true + } + return false +} + +// Get returns a byte from the buffer. If the buffer is empty, +// the method will return a false as the second value. +func (rb *RingBuffer) Get() (byte, bool) { + if rb.Used() != 0 { + rb.tail.Set(rb.tail.Get() + 1) + return rb.rxbuffer[rb.tail.Get()%bufferSize].Get(), true + } + return 0, false +} + +// Clear resets the head and tail pointer to zero. +func (rb *RingBuffer) Clear() { + rb.head.Set(0) + rb.tail.Set(0) +} + +const bufferSize2 = 8 + +// RingBuffer2 is ring buffer implementation inspired by post at +// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php +type RingBuffer2 struct { + rxbuffer [bufferSize2]struct { + buf [64]byte + size int + } + head volatile.Register8 + tail volatile.Register8 +} + +// NewRingBuffer returns a new ring buffer. +func NewRingBuffer2() *RingBuffer2 { + return &RingBuffer2{} +} + +// Used returns how many bytes in buffer have been used. +func (rb *RingBuffer2) Used() uint8 { + return uint8(rb.head.Get() - rb.tail.Get()) +} + +// Put stores a byte in the buffer. If the buffer is already +// full, the method will return false. +func (rb *RingBuffer2) Put(val []byte) bool { + if rb.Used() == bufferSize2 { + return false + } + + if rb.Used() == 0 { + rb.head.Set(rb.head.Get() + 1) + rb.rxbuffer[rb.head.Get()%bufferSize2].size = 0 + } + buf := &rb.rxbuffer[rb.head.Get()%bufferSize2] + + for i := 0; i < len(val); i++ { + if buf.size == 64 { + // next + rb.head.Set(rb.head.Get() + 1) + buf = &rb.rxbuffer[rb.head.Get()%bufferSize2] + rb.rxbuffer[rb.head.Get()%bufferSize2].size = 0 + } + buf.buf[buf.size] = val[i] + buf.size++ + } + return true +} + +// Get returns a byte from the buffer. If the buffer is empty, +// the method will return a false as the second value. +func (rb *RingBuffer2) Get() ([]byte, bool) { + if rb.Used() != 0 { + rb.tail.Set(rb.tail.Get() + 1) + size := rb.rxbuffer[rb.tail.Get()%bufferSize2].size + return rb.rxbuffer[rb.tail.Get()%bufferSize2].buf[:size], true + } + return nil, false +} + +// Clear resets the head and tail pointer to zero. +func (rb *RingBuffer2) Clear() { + rb.head.Set(0) + rb.tail.Set(0) +} diff --git a/src/machine/usb/cdc/cdc.go b/src/machine/usb/cdc/cdc.go new file mode 100644 index 000000000..1beea9772 --- /dev/null +++ b/src/machine/usb/cdc/cdc.go @@ -0,0 +1,164 @@ +package cdc + +import ( + "machine" +) + +const ( + cdcEndpointACM = 1 + cdcEndpointOut = 2 + cdcEndpointIn = 3 +) + +var CDC *cdc + +type cdc struct { + buf *RingBuffer + callbackFuncRx func([]byte) +} + +// New returns hid-mouse. +func New() *USBCDC { + USB = &USBCDC{ + Buffer: NewRingBuffer(), + Buffer2: NewRingBuffer2(), + } + return USB +} + +func newCDC() *cdc { + m := &cdc{ + buf: NewRingBuffer(), + } + //machine.EnableCDC(m.Callback, m.CallbackRx) + return m +} + +func (c *cdc) SetCallback(callbackRx func([]byte)) { + c.callbackFuncRx = callbackRx +} + +func (c *cdc) Write(b []byte) (n int, err error) { + i := 0 + for i = 0; i < len(b); i++ { + c.buf.Put(b[i]) + } + return i, nil +} + +func (c *cdc) sendUSBPacket(b []byte) { + machine.SendUSBInPacket(cdcEndpointIn, b) +} + +// from BulkIn +func (c *cdc) Callback() { + if b, ok := c.buf.Get(); ok { + c.sendUSBPacket([]byte{b}) + } +} + +// from BulkOut +func (c *cdc) CallbackRx(b []byte) { + if c.callbackFuncRx != nil { + c.callbackFuncRx(b) + } +} + +const ( + // bmRequestType + usb_REQUEST_HOSTTODEVICE = 0x00 + usb_REQUEST_DEVICETOHOST = 0x80 + usb_REQUEST_DIRECTION = 0x80 + + usb_REQUEST_STANDARD = 0x00 + usb_REQUEST_CLASS = 0x20 + usb_REQUEST_VENDOR = 0x40 + usb_REQUEST_TYPE = 0x60 + + usb_REQUEST_DEVICE = 0x00 + usb_REQUEST_INTERFACE = 0x01 + usb_REQUEST_ENDPOINT = 0x02 + usb_REQUEST_OTHER = 0x03 + usb_REQUEST_RECIPIENT = 0x1F + + usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE) + usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE = (usb_REQUEST_HOSTTODEVICE | usb_REQUEST_CLASS | usb_REQUEST_INTERFACE) + usb_REQUEST_DEVICETOHOST_STANDARD_INTERFACE = (usb_REQUEST_DEVICETOHOST | usb_REQUEST_STANDARD | usb_REQUEST_INTERFACE) + + // CDC Class requests + usb_CDC_SET_LINE_CODING = 0x20 + usb_CDC_GET_LINE_CODING = 0x21 + usb_CDC_SET_CONTROL_LINE_STATE = 0x22 + usb_CDC_SEND_BREAK = 0x23 + + usb_CDC_V1_10 = 0x0110 + usb_CDC_COMMUNICATION_INTERFACE_CLASS = 0x02 + + usb_CDC_CALL_MANAGEMENT = 0x01 + usb_CDC_ABSTRACT_CONTROL_MODEL = 0x02 + usb_CDC_HEADER = 0x00 + usb_CDC_ABSTRACT_CONTROL_MANAGEMENT = 0x02 + usb_CDC_UNION = 0x06 + usb_CDC_CS_INTERFACE = 0x24 + usb_CDC_CS_ENDPOINT = 0x25 + usb_CDC_DATA_INTERFACE_CLASS = 0x0A + + usb_CDC_LINESTATE_DTR = 0x01 + usb_CDC_LINESTATE_RTS = 0x02 +) + +func (c *cdc) handleSetup(setup machine.USBSetup) bool { + if setup.BmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE { + if setup.BRequest == usb_CDC_GET_LINE_CODING { + var b [cdcLineInfoSize]byte + 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) + + //c.sendUSBPacket(0, b[:], setup.WLength) + c.sendUSBPacket(b[:]) + return true + } + } + + if setup.BmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { + if setup.BRequest == usb_CDC_SET_LINE_CODING { + b, err := machine.ReceiveUSBControlPacket() + if err != nil { + return false + } + + 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 { + machine.ResetProcessor() + } else { + // TODO: cancel any reset + } + sendZlp() + } + + if setup.BRequest == usb_CDC_SEND_BREAK { + // TODO: something with this value? + // breakValue = ((uint16_t)setup.WValueH << 8) | setup.WValueL; + // return false; + sendZlp() + } + return true + } + return false +} diff --git a/src/machine/usb/cdc/usbcdc.go b/src/machine/usb/cdc/usbcdc.go new file mode 100644 index 000000000..5841163b1 --- /dev/null +++ b/src/machine/usb/cdc/usbcdc.go @@ -0,0 +1,225 @@ +package cdc + +import ( + "errors" + "machine" + "runtime/interrupt" + "runtime/volatile" +) + +var ( + errUSBCDCBufferEmpty = errors.New("USB-CDC buffer empty") + errUSBCDCWriteByteTimeout = errors.New("USB-CDC write byte timeout") + errUSBCDCReadTimeout = errors.New("USB-CDC read timeout") + errUSBCDCBytesRead = errors.New("USB-CDC invalid number of bytes read") +) + +const cdcLineInfoSize = 7 + +type cdcLineInfo struct { + dwDTERate uint32 + bCharFormat uint8 + bParityType uint8 + bDataBits uint8 + lineState uint8 +} + +// USBCDC is the serial interface that works over the USB port. +// To implement the USBCDC interface for a board, you must declare a concrete type as follows: +// +// type USBCDC struct { +// Buffer *RingBuffer +// } +// +// You can also add additional members to this struct depending on your implementation, +// but the *RingBuffer is required. +// When you are declaring the USBCDC for your board, make sure that you also declare the +// RingBuffer using the NewRingBuffer() function: +// +// USBCDC{Buffer: NewRingBuffer()} +// + +// Read from the RX buffer. +func (usbcdc *USBCDC) Read(data []byte) (n int, err error) { + // check if RX buffer is empty + size := usbcdc.Buffered() + if size == 0 { + return 0, nil + } + + // Make sure we do not read more from buffer than the data slice can hold. + if len(data) < size { + size = len(data) + } + + // only read number of bytes used from buffer + for i := 0; i < size; i++ { + v, _ := usbcdc.ReadByte() + data[i] = v + } + + return size, nil +} + +// ReadByte reads a single byte from the RX buffer. +// If there is no data in the buffer, returns an error. +func (usbcdc *USBCDC) ReadByte() (byte, error) { + // check if RX buffer is empty + buf, ok := usbcdc.Buffer.Get() + if !ok { + return 0, errUSBCDCBufferEmpty + } + return buf, nil +} + +// Buffered returns the number of bytes currently stored in the RX buffer. +func (usbcdc *USBCDC) Buffered() int { + return int(usbcdc.Buffer.Used()) +} + +// Receive handles adding data to the UART's data buffer. +// Usually called by the IRQ handler for a machine. +func (usbcdc *USBCDC) Receive(data byte) { + usbcdc.Buffer.Put(data) +} + +// USBCDC is the USB CDC aka serial over USB interface on the SAMD21. +type USBCDC struct { + Buffer *RingBuffer + Buffer2 *RingBuffer2 + TxIdx volatile.Register8 + waitTxcRetryCount uint8 + sent bool + configured bool + waitTxc bool +} + +func (x *USBCDC) Debug() int { + return 3 +} + +var ( + // USB is a USB CDC interface. + USB *USBCDC + + usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} +) + +// Configure the USB CDC interface. The config is here for compatibility with the UART interface. +func (usbcdc *USBCDC) Configure(config machine.UARTConfig) { +} + +// Flush flushes buffered data. +func (usbcdc *USBCDC) Flush() { + mask := interrupt.Disable() + if b, ok := usbcdc.Buffer2.Get(); ok { + machine.SendUSBInPacket(cdcEndpointIn, b) + } else { + usbcdc.waitTxc = false + } + interrupt.Restore(mask) +} + +// Write data to the USBCDC. +func (usbcdc *USBCDC) Write(data []byte) (n int, err error) { + if usbLineInfo.lineState > 0 { + mask := interrupt.Disable() + usbcdc.Buffer2.Put(data) + if !usbcdc.waitTxc { + usbcdc.waitTxc = true + usbcdc.Flush() + } + interrupt.Restore(mask) + } + return len(data), nil +} + +// WriteByte writes a byte of data to the USB CDC interface. +func (usbcdc *USBCDC) WriteByte(c byte) error { + usbcdc.Write([]byte{c}) + 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 +} + +// Configured returns whether usbcdc is configured or not. +func (usbcdc *USBCDC) Configured() bool { + return usbcdc.configured +} + +func cdcCallbackRx(b []byte) { + for i := range b { + USB.Receive(b[i]) + } +} + +func cdcSetup(setup machine.USBSetup) bool { + if setup.BmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE { + if setup.BRequest == usb_CDC_GET_LINE_CODING { + var b [cdcLineInfoSize]byte + 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) + + //machine.SendUSBPacket(0, b[:], setup.WLength) + machine.SendUSBInPacket(0, b[:]) + return true + } + } + + if setup.BmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { + if setup.BRequest == usb_CDC_SET_LINE_CODING { + b, err := machine.ReceiveUSBControlPacket() + if err != nil { + return false + } + + 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 { + machine.ResetProcessor() + } else { + // TODO: cancel any reset + } + sendZlp() + } + + if setup.BRequest == usb_CDC_SEND_BREAK { + // TODO: something with this value? + // breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL; + // return false; + sendZlp() + } + return true + } + return false +} + +func EnableUSBCDC() { + machine.Serial = USB + machine.EnableCDC(USB.Flush, cdcCallbackRx, cdcSetup) +} + +func sendZlp() { + machine.SendZlp() +} diff --git a/src/machine/usb/hid/hid.go b/src/machine/usb/hid/hid.go index fd9317511..80c712fb2 100644 --- a/src/machine/usb/hid/hid.go +++ b/src/machine/usb/hid/hid.go @@ -14,6 +14,9 @@ var ( const ( hidEndpoint = 4 + + usb_SET_REPORT_TYPE = 33 + usb_SET_IDLE = 10 ) type hidDevicer interface { @@ -27,7 +30,7 @@ var size int // calls machine.EnableHID for USB configuration func SetCallbackHandler(d hidDevicer) { if size == 0 { - machine.EnableHID(callback) + machine.EnableHID(callback, nil, nil) } devices[size] = d @@ -45,7 +48,16 @@ func callback() { } } +func callbackSetup(setup machine.USBSetup) bool { + ok := false + if setup.BmRequestType == usb_SET_REPORT_TYPE && setup.BRequest == usb_SET_IDLE { + machine.SendZlp() + ok = true + } + return ok +} + // SendUSBPacket sends a HIDPacket. func SendUSBPacket(b []byte) { - machine.SendUSBHIDPacket(hidEndpoint, b) + machine.SendUSBInPacket(hidEndpoint, b) } diff --git a/src/machine/usb/hid/keyboard/keyboard.go b/src/machine/usb/hid/keyboard/keyboard.go index 1f5643385..77dacf688 100644 --- a/src/machine/usb/hid/keyboard/keyboard.go +++ b/src/machine/usb/hid/keyboard/keyboard.go @@ -41,7 +41,8 @@ type keyboard struct { // wideChar holds high bits for the UTF-8 decoder. wideChar uint16 - buf *hid.RingBuffer + buf *hid.RingBuffer + waitTxc bool } // decodeState represents a state in the UTF-8 decode state machine. @@ -74,13 +75,24 @@ func newKeyboard() *keyboard { } func (kb *keyboard) Callback() bool { + kb.waitTxc = false if b, ok := kb.buf.Get(); ok { + kb.waitTxc = true hid.SendUSBPacket(b) return true } return false } +func (kb *keyboard) tx(b []byte) { + if kb.waitTxc { + kb.buf.Put(b) + } else { + kb.waitTxc = true + hid.SendUSBPacket(b) + } +} + func (kb *keyboard) ready() bool { return true } @@ -214,7 +226,7 @@ func (kb *keyboard) Press(c Keycode) error { } func (kb *keyboard) sendKey(consumer bool, b []byte) bool { - kb.buf.Put(b) + kb.tx(b) return true } diff --git a/src/machine/usb/hid/mouse/mouse.go b/src/machine/usb/hid/mouse/mouse.go index 49a189f50..bc079c96a 100644 --- a/src/machine/usb/hid/mouse/mouse.go +++ b/src/machine/usb/hid/mouse/mouse.go @@ -15,8 +15,9 @@ const ( ) type mouse struct { - buf *hid.RingBuffer - button Button + buf *hid.RingBuffer + button Button + waitTxc bool } func init() { @@ -38,13 +39,24 @@ func newMouse() *mouse { } func (m *mouse) Callback() bool { + m.waitTxc = false if b, ok := m.buf.Get(); ok { + m.waitTxc = true hid.SendUSBPacket(b[:5]) return true } return false } +func (m *mouse) tx(b []byte) { + if m.waitTxc { + m.buf.Put(b) + } else { + m.waitTxc = true + hid.SendUSBPacket(b) + } +} + // Move is a function that moves the mouse cursor. func (m *mouse) Move(vx, vy int) { if vx == 0 && vy == 0 { @@ -65,7 +77,7 @@ func (m *mouse) Move(vx, vy int) { vy = 127 } - m.buf.Put([]byte{ + m.tx([]byte{ 0x01, byte(m.button), byte(vx), byte(vy), 0x00, }) } @@ -79,7 +91,7 @@ func (m *mouse) Click(btn Button) { // Press presses the given mouse buttons. func (m *mouse) Press(btn Button) { m.button |= btn - m.buf.Put([]byte{ + m.tx([]byte{ 0x01, byte(m.button), 0x00, 0x00, 0x00, }) } @@ -87,7 +99,7 @@ func (m *mouse) Press(btn Button) { // Release releases the given mouse buttons. func (m *mouse) Release(btn Button) { m.button &= ^btn - m.buf.Put([]byte{ + m.tx([]byte{ 0x01, byte(m.button), 0x00, 0x00, 0x00, }) } @@ -105,7 +117,7 @@ func (m *mouse) Wheel(v int) { v = 127 } - m.buf.Put([]byte{ + m.tx([]byte{ 0x01, byte(m.button), 0x00, 0x00, byte(v), }) } diff --git a/src/machine/usb/midi/buffer.go b/src/machine/usb/midi/buffer.go new file mode 100644 index 000000000..39cab2ca6 --- /dev/null +++ b/src/machine/usb/midi/buffer.go @@ -0,0 +1,52 @@ +package midi + +import ( + "runtime/volatile" +) + +const bufferSize = 128 + +// RingBuffer is ring buffer implementation inspired by post at +// https://www.embeddedrelated.com/showthread/comp.arch.embedded/77084-1.php +type RingBuffer struct { + rxbuffer [bufferSize][4]byte + head volatile.Register8 + tail volatile.Register8 +} + +// NewRingBuffer returns a new ring buffer. +func NewRingBuffer() *RingBuffer { + return &RingBuffer{} +} + +// Used returns how many bytes in buffer have been used. +func (rb *RingBuffer) Used() uint8 { + return uint8(rb.head.Get() - rb.tail.Get()) +} + +// Put stores a byte in the buffer. If the buffer is already +// full, the method will return false. +func (rb *RingBuffer) Put(val []byte) bool { + if rb.Used() != bufferSize { + rb.head.Set(rb.head.Get() + 1) + copy(rb.rxbuffer[rb.head.Get()%bufferSize][:], val) + return true + } + return false +} + +// Get returns a byte from the buffer. If the buffer is empty, +// the method will return a false as the second value. +func (rb *RingBuffer) Get() ([]byte, bool) { + if rb.Used() != 0 { + rb.tail.Set(rb.tail.Get() + 1) + return rb.rxbuffer[rb.tail.Get()%bufferSize][:], true + } + return nil, false +} + +// Clear resets the head and tail pointer to zero. +func (rb *RingBuffer) Clear() { + rb.head.Set(0) + rb.tail.Set(0) +} diff --git a/src/machine/usb/midi/midi.go b/src/machine/usb/midi/midi.go new file mode 100644 index 000000000..afcee53c8 --- /dev/null +++ b/src/machine/usb/midi/midi.go @@ -0,0 +1,79 @@ +package midi + +import ( + "machine" +) + +const ( + midiEndpointOut = 5 // from PC + midiEndpointIn = 6 // to PC +) + +var Midi *midi + +type midi struct { + buf *RingBuffer + callbackFuncRx func([]byte) + waitTxc bool +} + +func init() { + if Midi == nil { + Midi = newMidi() + } +} + +// New returns hid-mouse. +func New() *midi { + return Midi +} + +func newMidi() *midi { + m := &midi{ + buf: NewRingBuffer(), + } + machine.EnableMIDI(m.Callback, m.CallbackRx, nil) + return m +} + +func (m *midi) SetCallback(callbackRx func([]byte)) { + m.callbackFuncRx = callbackRx +} + +func (m *midi) Write(b []byte) (n int, err error) { + i := 0 + for i = 0; i < len(b); i += 4 { + m.tx(b[i : i+4]) + } + return i, nil +} + +// sendUSBPacket sends a MIDIPacket. +func (m *midi) sendUSBPacket(b []byte) { + machine.SendUSBInPacket(midiEndpointIn, b) +} + +// from BulkIn +func (m *midi) Callback() { + m.waitTxc = false + if b, ok := m.buf.Get(); ok { + m.waitTxc = true + m.sendUSBPacket(b) + } +} + +func (m *midi) tx(b []byte) { + if m.waitTxc { + m.buf.Put(b) + } else { + m.waitTxc = true + m.sendUSBPacket(b) + } +} + +// from BulkOut +func (m *midi) CallbackRx(b []byte) { + if m.callbackFuncRx != nil { + m.callbackFuncRx(b) + } +} diff --git a/src/machine/usb_descriptor.go b/src/machine/usb_descriptor.go index 9a1220c42..c6d7ca36f 100644 --- a/src/machine/usb_descriptor.go +++ b/src/machine/usb_descriptor.go @@ -14,6 +14,9 @@ func (d *USBDescriptor) Configure(idVendor, idProduct uint16) { d.Device[9] = byte(idVendor >> 8) d.Device[10] = byte(idProduct) d.Device[11] = byte(idProduct >> 8) + + d.Configuration[2] = byte(len(d.Configuration)) + d.Configuration[3] = byte(len(d.Configuration) >> 8) } var descriptorCDC = USBDescriptor{ @@ -68,3 +71,35 @@ var descriptorCDCHID = USBDescriptor{ }, }, } + +var descriptorCDCMIDI = USBDescriptor{ + Device: []byte{ + 0x12, 0x01, 0x00, 0x02, 0xef, 0x02, 0x01, 0x40, 0x86, 0x28, 0x2d, 0x80, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01, + }, + Configuration: []byte{ + 0x09, 0x02, 0xaf, 0x00, 0x04, 0x01, 0x00, 0xa0, 0x32, + 0x08, 0x0b, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, + 0x05, 0x24, 0x00, 0x10, 0x01, + 0x04, 0x24, 0x02, 0x06, + 0x05, 0x24, 0x06, 0x00, 0x01, + 0x05, 0x24, 0x01, 0x01, 0x01, + 0x07, 0x05, 0x81, 0x03, 0x10, 0x00, 0x10, + 0x09, 0x04, 0x01, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00, + 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00, + 0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00, + 0x08, 0x0b, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, + 0x09, 0x04, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + 0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x03, + 0x09, 0x04, 0x03, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00, + 0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00, + 0x06, 0x24, 0x02, 0x01, 0x01, 0x00, + 0x06, 0x24, 0x02, 0x02, 0x02, 0x00, + 0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00, + 0x09, 0x24, 0x03, 0x02, 0x04, 0x01, 0x01, 0x01, 0x00, + 0x09, 0x05, 0x05, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x25, 0x01, 0x01, 0x01, + 0x09, 0x05, 0x86, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x25, 0x01, 0x01, 0x03, + }, +} diff --git a/src/runtime/runtime_atsamd21.go b/src/runtime/runtime_atsamd21.go index 694686556..55a926cff 100644 --- a/src/runtime/runtime_atsamd21.go +++ b/src/runtime/runtime_atsamd21.go @@ -7,6 +7,7 @@ import ( "device/arm" "device/sam" "machine" + "machine/usb/cdc" "runtime/interrupt" "runtime/volatile" "unsafe" @@ -29,10 +30,9 @@ func init() { initADCClock() // connect to USB CDC interface + cdc.EnableUSBCDC() + machine.USB.Configure(machine.UARTConfig{}) machine.Serial.Configure(machine.UARTConfig{}) - if !machine.USB.Configured() { - machine.USB.Configure(machine.UARTConfig{}) - } } func putchar(c byte) { diff --git a/src/runtime/runtime_atsamd51.go b/src/runtime/runtime_atsamd51.go index 8c7282fa2..81fe5707b 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/cdc" "runtime/interrupt" "runtime/volatile" ) @@ -29,10 +30,9 @@ func init() { initADCClock() // connect to USB CDC interface + cdc.EnableUSBCDC() + machine.USB.Configure(machine.UARTConfig{}) machine.Serial.Configure(machine.UARTConfig{}) - if !machine.USB.Configured() { - machine.USB.Configure(machine.UARTConfig{}) - } } func putchar(c byte) { diff --git a/src/runtime/runtime_nrf.go b/src/runtime/runtime_nrf.go index 7ee34f8be..4263b9c1b 100644 --- a/src/runtime/runtime_nrf.go +++ b/src/runtime/runtime_nrf.go @@ -7,6 +7,7 @@ import ( "device/arm" "device/nrf" "machine" + "machine/usb/cdc" "runtime/interrupt" "runtime/volatile" ) @@ -28,6 +29,9 @@ func main() { } func init() { + // connect to USB CDC interface + cdc.EnableUSBCDC() + machine.USB.Configure(machine.UARTConfig{}) machine.Serial.Configure(machine.UARTConfig{}) initLFCLK() initRTC()