diff options
author | Ron Evans <[email protected]> | 2019-02-21 20:29:12 +0100 |
---|---|---|
committer | Ayke van Laethem <[email protected]> | 2019-02-23 13:34:00 +0100 |
commit | 5438f16fcb1b541715b0717d79d78fb75123a18a (patch) | |
tree | 0ad644a6c1e5c3745f04d36d39dcbb128760a41b | |
parent | 7f027ddd330a0851faf917ac4d6c71f043348f6b (diff) | |
download | tinygo-5438f16fcb1b541715b0717d79d78fb75123a18a.tar.gz tinygo-5438f16fcb1b541715b0717d79d78fb75123a18a.zip |
machine/atsamd21: support for USB CDC aka serial interface
Signed-off-by: Ron Evans <[email protected]>
-rw-r--r-- | src/machine/board_itsybitsy-m0.go | 8 | ||||
-rw-r--r-- | src/machine/machine_atsamd21.go | 933 | ||||
-rw-r--r-- | src/machine/usb.go | 599 | ||||
-rw-r--r-- | src/runtime/runtime_atsamd21.go | 14 |
4 files changed, 1544 insertions, 10 deletions
diff --git a/src/machine/board_itsybitsy-m0.go b/src/machine/board_itsybitsy-m0.go index d43f870b5..8dc4977bf 100644 --- a/src/machine/board_itsybitsy-m0.go +++ b/src/machine/board_itsybitsy-m0.go @@ -34,7 +34,13 @@ const ( LED = D13 ) -// UART0 pins +// UART0 aka USBCDC pins +const ( + USBCDC_DM_PIN = 24 + USBCDC_DP_PIN = 25 +) + +// UART1 pins const ( UART_TX_PIN = D1 UART_RX_PIN = D0 diff --git a/src/machine/machine_atsamd21.go b/src/machine/machine_atsamd21.go index 608dffba2..183801cb8 100644 --- a/src/machine/machine_atsamd21.go +++ b/src/machine/machine_atsamd21.go @@ -8,9 +8,12 @@ package machine import ( + "bytes" "device/arm" "device/sam" + "encoding/binary" "errors" + "unsafe" ) const CPU_FREQUENCY = 48000000 @@ -57,6 +60,19 @@ func (p GPIO) Configure(config GPIOConfig) { } // enable port config p.setPinCfg(sam.PORT_PINCFG0_PMUXEN | sam.PORT_PINCFG0_DRVSTR | sam.PORT_PINCFG0_INEN) + + case GPIO_COM: + if p.Pin&1 > 0 { + // odd pin, so save the even pins + val := p.getPMux() & sam.PORT_PMUX0_PMUXE_Msk + p.setPMux(val | (GPIO_COM << sam.PORT_PMUX0_PMUXO_Pos)) + } else { + // even pin, so save the odd pins + val := p.getPMux() & sam.PORT_PMUX0_PMUXO_Msk + p.setPMux(val | (GPIO_COM << sam.PORT_PMUX0_PMUXE_Pos)) + } + // enable port config + p.setPinCfg(sam.PORT_PINCFG0_PMUXEN) } } @@ -102,11 +118,14 @@ type UART struct { } var ( + // UART0 is actually a USB CDC interface. + UART0 = USBCDC{Buffer: NewRingBuffer()} + // The first hardware serial port on the SAMD21. Uses the SERCOM0 interface. - UART0 = UART{Bus: sam.SERCOM0_USART, Buffer: NewRingBuffer()} + UART1 = UART{Bus: sam.SERCOM0_USART, Buffer: NewRingBuffer()} // The second hardware serial port on the SAMD21. Uses the SERCOM1 interface. - UART1 = UART{Bus: sam.SERCOM1_USART, Buffer: NewRingBuffer()} + UART2 = UART{Bus: sam.SERCOM1_USART, Buffer: NewRingBuffer()} ) const ( @@ -249,17 +268,17 @@ func (uart UART) WriteByte(c byte) error { } //go:export SERCOM0_IRQHandler -func handleUART0() { +func handleUART1() { // should reset IRQ - UART0.Receive(byte((UART0.Bus.DATA & 0xFF))) - UART0.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC + UART1.Receive(byte((UART1.Bus.DATA & 0xFF))) + UART1.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC } //go:export SERCOM1_IRQHandler -func handleUART1() { +func handleUART2() { // should reset IRQ - UART1.Receive(byte((UART1.Bus.DATA & 0xFF))) - UART1.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC + UART2.Receive(byte((UART2.Bus.DATA & 0xFF))) + UART2.Bus.INTFLAG |= sam.SERCOM_USART_INTFLAG_RXC } // I2C on the SAMD21. @@ -906,3 +925,901 @@ func (pwm PWM) setChannel(val sam.RegValue) { return // not supported on this pin } } + +// USBCDC is the USB CDC aka serial over USB interface on the SAMD21. +type USBCDC struct { + Buffer *RingBuffer +} + +// WriteByte writes a byte of data to the USB CDC interface. +func (usbcdc USBCDC) WriteByte(c byte) error { + // Supposedly to handle problem with Windows USB serial ports? + if usbLineInfo.lineState > 0 { + // set the data + udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN][0] = c + + usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].ADDR = + sam.RegValue(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[usb_CDC_ENDPOINT_IN]))) + + // clean multi packet size of bytes already sent + usbEndpointDescriptors[usb_CDC_ENDPOINT_IN].DeviceDescBank[1].PCKSIZE &^= + sam.RegValue(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 |= + sam.RegValue((1&usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask)<<usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) | + sam.RegValue(epPacketSize(64)<<usb_DEVICE_PCKSIZE_SIZE_Pos) + + // ack 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) + + // wait for transfer to complete + for (getEPINTFLAG(usb_CDC_ENDPOINT_IN) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 { + } + } + + return nil +} + +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 |= sam.USB_DEVICE_CTRLA_SWRST + for (sam.USB_DEVICE.SYNCBUSY&sam.USB_DEVICE_SYNCBUSY_SWRST) > 0 || + (sam.USB_DEVICE.SYNCBUSY&sam.USB_DEVICE_SYNCBUSY_ENABLE) > 0 { + } + + sam.USB_DEVICE.DESCADD = sam.RegValue(uintptr(unsafe.Pointer(&usbEndpointDescriptors))) + + // configure pins + GPIO{USBCDC_DM_PIN}.Configure(GPIOConfig{Mode: GPIO_COM}) + GPIO{USBCDC_DP_PIN}.Configure(GPIOConfig{Mode: GPIO_COM}) + + // performs pad calibration from store fuses + handlePadCalibration() + + // run in standby + sam.USB_DEVICE.CTRLA |= sam.USB_DEVICE_CTRLA_RUNSTDBY + + // set full speed + sam.USB_DEVICE.CTRLB |= (sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos) + + // attach + sam.USB_DEVICE.CTRLB &^= sam.USB_DEVICE_CTRLB_DETACH + + // enable interrupt for end of reset + sam.USB_DEVICE.INTENSET |= sam.USB_DEVICE_INTENSET_EORST + + // enable interrupt for start of frame + sam.USB_DEVICE.INTENSET |= sam.USB_DEVICE_INTENSET_SOF + + // enable USB + sam.USB_DEVICE.CTRLA |= sam.USB_DEVICE_CTRLA_ENABLE + + // enable IRQ + arm.EnableIRQ(sam.IRQ_USB) +} + +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 := sam.RegValue16(uint16(fuse>>13) & uint16(0x1f)) + calibTransP := sam.RegValue16(uint16(fuse>>18) & uint16(0x1f)) + calibTrim := sam.RegValue16(uint16(fuse>>23) & uint16(0x7)) + + if calibTransN == 0x1f { + calibTransN = 5 + } + sam.USB_DEVICE.PADCAL |= (calibTransN << sam.USB_DEVICE_PADCAL_TRANSN_Pos) + + if calibTransP == 0x1f { + calibTransP = 29 + } + sam.USB_DEVICE.PADCAL |= (calibTransP << sam.USB_DEVICE_PADCAL_TRANSP_Pos) + + if calibTrim == 0x7 { + calibTransN = 3 + } + sam.USB_DEVICE.PADCAL |= (calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos) +} + +//go:export USB_IRQHandler +func handleUSB() { + // reset all interrupt flags + flags := sam.USB_DEVICE.INTFLAG + sam.USB_DEVICE.INTFLAG = 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 = 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) + + 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) + } + } + + 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 + eptInts := sam.USB_DEVICE.EPINTSMRY & 0xFE // Remove endpoint number 0 (setup) + var i uint32 + for i = 1; i < uint32(len(endPoints)); i++ { + // Check if endpoint has a pending interrupt + if eptInts&(1<<i) > 0 { + // yes, so handle flags + epFlags := getEPINTFLAG(i) + setEPINTFLAG(i, epFlags) + + // Endpoint Transfer Complete Interrupt + if (epFlags & sam.USB_DEVICE_EPINTFLAG_TRCPT0) > 0 { + handleEndpoint(i) + } + } + } +} + +func initEndpoint(ep, config uint32) { + switch config { + case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn: + // set packet size + usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE |= + sam.RegValue(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR = + sam.RegValue(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))) + + // set endpoint type + setEPCFG(ep, getEPCFG(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 |= + sam.RegValue(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR = + sam.RegValue(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))) + + // set endpoint type + setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_BULK+1)<<sam.USB_DEVICE_EPCFG_EPTYPE0_Pos)) + + // ack the current transfer + setEPINTENSET(ep, sam.USB_DEVICE_EPINTENSET_TRCPT0) + + // 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 |= + sam.RegValue(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR = + sam.RegValue(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))) + + // set endpoint type + setEPCFG(ep, getEPCFG(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 |= + sam.RegValue(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR = + sam.RegValue(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))) + + // set endpoint type + setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE0_Pos)) + + // Control IN + // set packet size + usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE |= + sam.RegValue(epPacketSize(64) << usb_DEVICE_PCKSIZE_SIZE_Pos) + + // set data buffer address + usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR = + sam.RegValue(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))) + + // set endpoint type + setEPCFG(ep, getEPCFG(ep)|((usb_ENDPOINT_TYPE_CONTROL+1)<<sam.USB_DEVICE_EPCFG_EPTYPE1_Pos)) + + // Prepare OUT endpoint for receive + // set multi packet size for expected number of receive bytes on control OUT + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE |= + sam.RegValue(64 << usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) + + // set byte count to zero, we have not received anything yet + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE &^= + sam.RegValue(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) + + // NAK on endpoint OUT to show we are ready to receive control data + setEPSTATUSSET(ep, sam.USB_DEVICE_EPSTATUSSET_BK0RDY) + } +} + +func handleStandardSetup(setup usbSetup) bool { + switch setup.bRequest { + case usb_GET_STATUS: + buf := []byte{0, 0} + + if setup.bmRequestType != 0 { // endpoint + // TODO: actually check if the endpoint in question is currently halted + if isEndpointHalt { + buf[0] = 1 + } + } + + sendUSBPacket(0, buf) + return true + + case usb_CLEAR_FEATURE: + if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP + isRemoteWakeUpEnabled = false + } else if setup.wValueL == 0 { // ENDPOINTHALT + isEndpointHalt = false + } + sendZlp(0) + return true + + case usb_SET_FEATURE: + if setup.wValueL == 1 { // DEVICEREMOTEWAKEUP + isRemoteWakeUpEnabled = true + } else if setup.wValueL == 0 { // ENDPOINTHALT + isEndpointHalt = true + } + sendZlp(0) + return true + + case usb_SET_ADDRESS: + // set packet size 64 with auto Zlp after transfer + usbEndpointDescriptors[0].DeviceDescBank[1].PCKSIZE = + sam.RegValue(epPacketSize(64)<<usb_DEVICE_PCKSIZE_SIZE_Pos) | + sam.RegValue(1<<31) // autozlp + + // ack the transfer is complete from the request + setEPINTFLAG(0, sam.USB_DEVICE_EPINTFLAG_TRCPT1) + + // set bank ready for data + setEPSTATUSSET(0, sam.USB_DEVICE_EPSTATUSSET_BK1RDY) + + // wait for transfer to complete + for (getEPINTFLAG(0) & sam.USB_DEVICE_EPINTFLAG_TRCPT1) == 0 { + } + + // last, set the device address to that requested by host + sam.USB_DEVICE.DADD |= sam.RegValue8(setup.wValueL) + sam.USB_DEVICE.DADD |= sam.USB_DEVICE_DADD_ADDEN + + return true + + case usb_GET_DESCRIPTOR: + sendDescriptor(setup) + return true + + case usb_SET_DESCRIPTOR: + return false + + case usb_GET_CONFIGURATION: + buff := []byte{usbConfiguration} + sendUSBPacket(0, buff) + return true + + case usb_SET_CONFIGURATION: + if setup.bmRequestType&usb_REQUEST_RECIPIENT == usb_REQUEST_DEVICE { + for i := 1; i < len(endPoints); i++ { + initEndpoint(uint32(i), endPoints[i]) + } + + usbConfiguration = setup.wValueL + + // Enable interrupt for CDC control messages from host (OUT packet) + setEPINTENSET(usb_CDC_ENDPOINT_ACM, sam.USB_DEVICE_EPINTENSET_TRCPT1) + + // Enable interrupt for CDC data messages from host + setEPINTENSET(usb_CDC_ENDPOINT_OUT, sam.USB_DEVICE_EPINTENSET_TRCPT0) + + sendZlp(0) + return true + } else { + return false + } + + case usb_GET_INTERFACE: + buff := []byte{usbSetInterface} + sendUSBPacket(0, buff) + return true + + case usb_SET_INTERFACE: + usbSetInterface = setup.wValueL + + sendZlp(0) + 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 { + buf := bytes.NewBuffer(make([]byte, 0, 7)) + binary.Write(buf, binary.LittleEndian, usbLineInfo.dwDTERate) + binary.Write(buf, binary.LittleEndian, usbLineInfo.bCharFormat) + binary.Write(buf, binary.LittleEndian, usbLineInfo.bParityType) + binary.Write(buf, binary.LittleEndian, usbLineInfo.bDataBits) + + sendUSBPacket(0, buf.Bytes()) + return true + } + } + + if setup.bmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { + if setup.bRequest == usb_CDC_SET_LINE_CODING { + buf := bytes.NewBuffer(receiveUSBControlPacket()) + binary.Read(buf, binary.LittleEndian, &(usbLineInfo.dwDTERate)) + binary.Read(buf, binary.LittleEndian, &(usbLineInfo.bCharFormat)) + binary.Read(buf, binary.LittleEndian, &(usbLineInfo.bParityType)) + binary.Read(buf, binary.LittleEndian, &(usbLineInfo.bDataBits)) + } + + 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&0x01) == 0 { + // TODO: system reset + } else { + // TODO: cancel any reset + } + } + + if setup.bRequest == usb_CDC_SEND_BREAK { + // TODO: something with this value? + // breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL; + // return false; + } + return true + } + return false +} + +func sendUSBPacket(ep uint32, data []byte) { + copy(udd_ep_in_cache_buffer[ep][:], data) + + // Set endpoint address for sending data + usbEndpointDescriptors[ep].DeviceDescBank[1].ADDR = + sam.RegValue(uintptr(unsafe.Pointer(&udd_ep_in_cache_buffer[ep]))) + + // clear multi-packet size which is total bytes already sent + usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE &^= + sam.RegValue(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 |= + sam.RegValue((len(data) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) +} + +func receiveUSBControlPacket() []byte { + // set ready to receive data + setEPSTATUSCLR(0, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) + + // read the data + bytesread := armRecvCtrlOUT(0) + + // return the data + data := make([]byte, 0, bytesread) + copy(data, udd_ep_out_cache_buffer[0][:bytesread]) + return data +} + +func armRecvCtrlOUT(ep uint32) uint32 { + // Set output address to receive data + usbEndpointDescriptors[ep].DeviceDescBank[0].ADDR = + sam.RegValue(uintptr(unsafe.Pointer(&udd_ep_out_cache_buffer[ep]))) + + // set multi-packet size which is total expected number of bytes to receive. + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE |= + sam.RegValue(8<<usb_DEVICE_PCKSIZE_MULTI_PACKET_SIZE_Pos) | + sam.RegValue(epPacketSize(64)<<usb_DEVICE_PCKSIZE_SIZE_Pos) + + // clear byte count of bytes received so far. + usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE &^= + sam.RegValue(usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask << usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) + + // clear ready state to start transfer + setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) + + // Wait until OUT transfer is ready. + for (getEPSTATUS(ep) & sam.USB_DEVICE_EPSTATUS_BK0RDY) == 0 { + } + + // Wait until OUT transfer is completed. + for (getEPINTFLAG(ep) & sam.USB_DEVICE_EPINTFLAG_TRCPT0) == 0 { + } + + // return number of bytes received + return uint32((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE >> + usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) +} + +// sendDescriptor creates and sends the various USB descriptor types that +// can be requested by the host. +func sendDescriptor(setup usbSetup) { + switch setup.wValueH { + case usb_CONFIGURATION_DESCRIPTOR_TYPE: + sendConfiguration(setup) + return + case usb_DEVICE_DESCRIPTOR_TYPE: + if setup.wLength == 8 { + // composite descriptor requested, so only send 8 bytes + dd := NewDeviceDescriptor(0xEF, 0x02, 0x01, 64, usb_VID, usb_PID, 0x100, usb_IMANUFACTURER, usb_IPRODUCT, usb_ISERIAL, 1) + sendUSBPacket(0, dd.Bytes()[:8]) + } else { + // complete descriptor requested so send entire packet + dd := NewDeviceDescriptor(0x00, 0x00, 0x00, 64, usb_VID, usb_PID, 0x100, usb_IMANUFACTURER, usb_IPRODUCT, usb_ISERIAL, 1) + sendUSBPacket(0, dd.Bytes()) + } + return + + case usb_STRING_DESCRIPTOR_TYPE: + switch setup.wValueL { + case 0: + b := make([]byte, 4) + b[0] = byte(usb_STRING_LANGUAGE[0] >> 8) + b[1] = byte(usb_STRING_LANGUAGE[0] & 0xff) + b[2] = byte(usb_STRING_LANGUAGE[1] >> 8) + b[3] = byte(usb_STRING_LANGUAGE[1] & 0xff) + sendUSBPacket(0, b) + + case usb_IPRODUCT: + prod := []byte(usb_STRING_PRODUCT) + b := make([]byte, len(prod)*2+2) + b[0] = byte(len(prod)*2 + 2) + b[1] = 0x03 + + for i, val := range prod { + b[i*2] = 0 + b[i*2+1] = val + } + + sendUSBPacket(0, b) + + case usb_IMANUFACTURER: + prod := []byte(usb_STRING_MANUFACTURER) + b := make([]byte, len(prod)*2+2) + b[0] = byte(len(prod)*2 + 2) + b[1] = 0x03 + + for i, val := range prod { + b[i*2] = 0 + b[i*2+1] = val + } + + sendUSBPacket(0, b) + + case usb_ISERIAL: + // TODO: allow returning a product serial number + sendZlp(0) + } + + // send final zero length packet and return + sendZlp(0) + return + } + + // do not know how to handle this message, so return zero + sendZlp(0) + return +} + +// sendConfiguration creates and sends the configuration packet to the host. +func sendConfiguration(setup usbSetup) { + if setup.wLength == 9 { + sz := uint16(configDescriptorSize + cdcSize) + config := NewConfigDescriptor(sz, 2) + sendUSBPacket(0, config.Bytes()) + } else { + iad := NewIADDescriptor(0, 2, usb_CDC_COMMUNICATION_INTERFACE_CLASS, usb_CDC_ABSTRACT_CONTROL_MODEL, 0) + + cif := NewInterfaceDescriptor(usb_CDC_ACM_INTERFACE, 1, usb_CDC_COMMUNICATION_INTERFACE_CLASS, usb_CDC_ABSTRACT_CONTROL_MODEL, 0) + + header := NewCDCCSInterfaceDescriptor(usb_CDC_HEADER, usb_CDC_V1_10&0xFF, (usb_CDC_V1_10>>8)&0x0FF) + + controlManagement := NewACMFunctionalDescriptor(usb_CDC_ABSTRACT_CONTROL_MANAGEMENT, 6) + + functionalDescriptor := NewCDCCSInterfaceDescriptor(usb_CDC_UNION, usb_CDC_ACM_INTERFACE, usb_CDC_DATA_INTERFACE) + + callManagement := NewCMFunctionalDescriptor(usb_CDC_CALL_MANAGEMENT, 1, 1) + + cifin := NewEndpointDescriptor((usb_CDC_ENDPOINT_ACM | usbEndpointIn), usb_ENDPOINT_TYPE_INTERRUPT, 0x10, 0x10) + + dif := NewInterfaceDescriptor(usb_CDC_DATA_INTERFACE, 2, usb_CDC_DATA_INTERFACE_CLASS, 0, 0) + + in := NewEndpointDescriptor((usb_CDC_ENDPOINT_OUT | usbEndpointOut), usb_ENDPOINT_TYPE_BULK, usbEndpointPacketSize, 0) + + out := NewEndpointDescriptor((usb_CDC_ENDPOINT_IN | usbEndpointIn), usb_ENDPOINT_TYPE_BULK, usbEndpointPacketSize, 0) + + cdc := NewCDCDescriptor(iad, + cif, + header, + controlManagement, + functionalDescriptor, + callManagement, + cifin, + dif, + in, + out) + + sz := uint16(configDescriptorSize + cdcSize) + config := NewConfigDescriptor(sz, 2) + + buf := make([]byte, 0, sz) + buf = append(buf, config.Bytes()...) + buf = append(buf, cdc.Bytes()...) + + sendUSBPacket(0, buf) + } +} + +func handleEndpoint(ep uint32) { + // get data + count := int((usbEndpointDescriptors[ep].DeviceDescBank[0].PCKSIZE >> + usb_DEVICE_PCKSIZE_BYTE_COUNT_Pos) & usb_DEVICE_PCKSIZE_BYTE_COUNT_Mask) + + // move to ring buffer + for i := 0; i < count; i++ { + UART0.Receive(byte((udd_ep_out_cache_buffer[ep][i] & 0xFF))) + } + + // set ready for next data + setEPSTATUSCLR(ep, sam.USB_DEVICE_EPSTATUSCLR_BK0RDY) + +} + +func sendZlp(ep uint32) { + usbEndpointDescriptors[ep].DeviceDescBank[1].PCKSIZE &^= + sam.RegValue(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) sam.RegValue8 { + switch ep { + case 0: + return sam.USB_DEVICE.EPCFG0 + case 1: + return sam.USB_DEVICE.EPCFG1 + case 2: + return sam.USB_DEVICE.EPCFG2 + case 3: + return sam.USB_DEVICE.EPCFG3 + case 4: + return sam.USB_DEVICE.EPCFG4 + case 5: + return sam.USB_DEVICE.EPCFG5 + case 6: + return sam.USB_DEVICE.EPCFG6 + case 7: + return sam.USB_DEVICE.EPCFG7 + default: + return 0 + } +} + +func setEPCFG(ep uint32, val sam.RegValue8) { + switch ep { + case 0: + sam.USB_DEVICE.EPCFG0 = val + case 1: + sam.USB_DEVICE.EPCFG1 = val + case 2: + sam.USB_DEVICE.EPCFG2 = val + case 3: + sam.USB_DEVICE.EPCFG3 = val + case 4: + sam.USB_DEVICE.EPCFG4 = val + case 5: + sam.USB_DEVICE.EPCFG5 = val + case 6: + sam.USB_DEVICE.EPCFG6 = val + case 7: + sam.USB_DEVICE.EPCFG7 = val + default: + return + } +} + +func setEPSTATUSCLR(ep uint32, val sam.RegValue8) { + switch ep { + case 0: + sam.USB_DEVICE.EPSTATUSCLR0 = val + case 1: + sam.USB_DEVICE.EPSTATUSCLR1 = val + case 2: + sam.USB_DEVICE.EPSTATUSCLR2 = val + case 3: + sam.USB_DEVICE.EPSTATUSCLR3 = val + case 4: + sam.USB_DEVICE.EPSTATUSCLR4 = val + case 5: + sam.USB_DEVICE.EPSTATUSCLR5 = val + case 6: + sam.USB_DEVICE.EPSTATUSCLR6 = val + case 7: + sam.USB_DEVICE.EPSTATUSCLR7 = val + default: + return + } +} + +func setEPSTATUSSET(ep uint32, val sam.RegValue8) { + switch ep { + case 0: + sam.USB_DEVICE.EPSTATUSSET0 = val + case 1: + sam.USB_DEVICE.EPSTATUSSET1 = val + case 2: + sam.USB_DEVICE.EPSTATUSSET2 = val + case 3: + sam.USB_DEVICE.EPSTATUSSET3 = val + case 4: + sam.USB_DEVICE.EPSTATUSSET4 = val + case 5: + sam.USB_DEVICE.EPSTATUSSET5 = val + case 6: + sam.USB_DEVICE.EPSTATUSSET6 = val + case 7: + sam.USB_DEVICE.EPSTATUSSET7 = val + default: + return + } +} + +func getEPSTATUS(ep uint32) sam.RegValue8 { + switch ep { + case 0: + return sam.USB_DEVICE.EPSTATUS0 + case 1: + return sam.USB_DEVICE.EPSTATUS1 + case 2: + return sam.USB_DEVICE.EPSTATUS2 + case 3: + return sam.USB_DEVICE.EPSTATUS3 + case 4: + return sam.USB_DEVICE.EPSTATUS4 + case 5: + return sam.USB_DEVICE.EPSTATUS5 + case 6: + return sam.USB_DEVICE.EPSTATUS6 + case 7: + return sam.USB_DEVICE.EPSTATUS7 + default: + return 0 + } +} + +func getEPINTFLAG(ep uint32) sam.RegValue8 { + switch ep { + case 0: + return sam.USB_DEVICE.EPINTFLAG0 + case 1: + return sam.USB_DEVICE.EPINTFLAG1 + case 2: + return sam.USB_DEVICE.EPINTFLAG2 + case 3: + return sam.USB_DEVICE.EPINTFLAG3 + case 4: + return sam.USB_DEVICE.EPINTFLAG4 + case 5: + return sam.USB_DEVICE.EPINTFLAG5 + case 6: + return sam.USB_DEVICE.EPINTFLAG6 + case 7: + return sam.USB_DEVICE.EPINTFLAG7 + default: + return 0 + } +} + +func setEPINTFLAG(ep uint32, val sam.RegValue8) { + switch ep { + case 0: + sam.USB_DEVICE.EPINTFLAG0 = val + case 1: + sam.USB_DEVICE.EPINTFLAG1 = val + case 2: + sam.USB_DEVICE.EPINTFLAG2 = val + case 3: + sam.USB_DEVICE.EPINTFLAG3 = val + case 4: + sam.USB_DEVICE.EPINTFLAG4 = val + case 5: + sam.USB_DEVICE.EPINTFLAG5 = val + case 6: + sam.USB_DEVICE.EPINTFLAG6 = val + case 7: + sam.USB_DEVICE.EPINTFLAG7 = val + default: + return + } +} + +func setEPINTENCLR(ep uint32, val sam.RegValue8) { + switch ep { + case 0: + sam.USB_DEVICE.EPINTENCLR0 = val + case 1: + sam.USB_DEVICE.EPINTENCLR1 = val + case 2: + sam.USB_DEVICE.EPINTENCLR2 = val + case 3: + sam.USB_DEVICE.EPINTENCLR3 = val + case 4: + sam.USB_DEVICE.EPINTENCLR4 = val + case 5: + sam.USB_DEVICE.EPINTENCLR5 = val + case 6: + sam.USB_DEVICE.EPINTENCLR6 = val + case 7: + sam.USB_DEVICE.EPINTENCLR7 = val + default: + return + } +} + +func setEPINTENSET(ep uint32, val sam.RegValue8) { + switch ep { + case 0: + sam.USB_DEVICE.EPINTENSET0 = val + case 1: + sam.USB_DEVICE.EPINTENSET1 = val + case 2: + sam.USB_DEVICE.EPINTENSET2 = val + case 3: + sam.USB_DEVICE.EPINTENSET3 = val + case 4: + sam.USB_DEVICE.EPINTENSET4 = val + case 5: + sam.USB_DEVICE.EPINTENSET5 = val + case 6: + sam.USB_DEVICE.EPINTENSET6 = val + case 7: + sam.USB_DEVICE.EPINTENSET7 = val + default: + return + } +} diff --git a/src/machine/usb.go b/src/machine/usb.go new file mode 100644 index 000000000..75fe183f2 --- /dev/null +++ b/src/machine/usb.go @@ -0,0 +1,599 @@ +// +build sam + +package machine + +import ( + "bytes" + "device/sam" + "encoding/binary" + "errors" +) + +const deviceDescriptorSize = 18 + +// DeviceDescriptor implements the USB standard device descriptor. +// +// Table 9-8. Standard Device Descriptor +// bLength, bDescriptorType, bcdUSB, bDeviceClass, bDeviceSubClass, bDeviceProtocol, bMaxPacketSize0, +// idVendor, idProduct, bcdDevice, iManufacturer, iProduct, iSerialNumber, bNumConfigurations */ +// +type DeviceDescriptor struct { + bLength uint8 // 18 + bDescriptorType uint8 // 1 USB_DEVICE_DESCRIPTOR_TYPE + bcdUSB uint16 // 0x200 + bDeviceClass uint8 + bDeviceSubClass uint8 + bDeviceProtocol uint8 + bMaxPacketSize0 uint8 // Packet 0 + idVendor uint16 + idProduct uint16 + bcdDevice uint16 // 0x100 + iManufacturer uint8 + iProduct uint8 + iSerialNumber uint8 + bNumConfigurations uint8 +} + +// NewDeviceDescriptor returns a USB DeviceDescriptor. +func NewDeviceDescriptor(class, subClass, proto, packetSize0 uint8, vid, pid, version uint16, im, ip, is, configs uint8) DeviceDescriptor { + return DeviceDescriptor{deviceDescriptorSize, 1, 0x200, class, subClass, proto, packetSize0, vid, pid, version, im, ip, is, configs} +} + +// Bytes returns DeviceDescriptor data +func (d DeviceDescriptor) Bytes() []byte { + buf := bytes.NewBuffer(make([]byte, 0, deviceDescriptorSize)) + binary.Write(buf, binary.LittleEndian, d.bLength) + binary.Write(buf, binary.LittleEndian, d.bDescriptorType) + binary.Write(buf, binary.LittleEndian, d.bcdUSB) + binary.Write(buf, binary.LittleEndian, d.bDeviceClass) + binary.Write(buf, binary.LittleEndian, d.bDeviceSubClass) + binary.Write(buf, binary.LittleEndian, d.bDeviceProtocol) + binary.Write(buf, binary.LittleEndian, d.bMaxPacketSize0) + binary.Write(buf, binary.LittleEndian, d.idVendor) + binary.Write(buf, binary.LittleEndian, d.idProduct) + binary.Write(buf, binary.LittleEndian, d.bcdDevice) + binary.Write(buf, binary.LittleEndian, d.iManufacturer) + binary.Write(buf, binary.LittleEndian, d.iProduct) + binary.Write(buf, binary.LittleEndian, d.iSerialNumber) + binary.Write(buf, binary.LittleEndian, d.bNumConfigurations) + return buf.Bytes() +} + +const configDescriptorSize = 9 + +// ConfigDescriptor implements the standard USB configuration descriptor. +// +// Table 9-10. Standard Configuration Descriptor +// bLength, bDescriptorType, wTotalLength, bNumInterfaces, bConfigurationValue, iConfiguration +// bmAttributes, bMaxPower +// +type ConfigDescriptor struct { + bLength uint8 // 9 + bDescriptorType uint8 // 2 + wTotalLength uint16 // total length + bNumInterfaces uint8 + bConfigurationValue uint8 + iConfiguration uint8 + bmAttributes uint8 + bMaxPower uint8 +} + +// NewConfigDescriptor returns a new USB ConfigDescriptor. +func NewConfigDescriptor(totalLength uint16, interfaces uint8) ConfigDescriptor { + return ConfigDescriptor{configDescriptorSize, 2, totalLength, interfaces, 1, 0, usb_CONFIG_BUS_POWERED | usb_CONFIG_REMOTE_WAKEUP, 50} +} + +// Bytes returns ConfigDescriptor data. +func (d ConfigDescriptor) Bytes() []byte { + buf := bytes.NewBuffer(make([]byte, 0, configDescriptorSize)) + binary.Write(buf, binary.LittleEndian, d.bLength) + binary.Write(buf, binary.LittleEndian, d.bDescriptorType) + binary.Write(buf, binary.LittleEndian, d.wTotalLength) + binary.Write(buf, binary.LittleEndian, d.bNumInterfaces) + binary.Write(buf, binary.LittleEndian, d.bConfigurationValue) + binary.Write(buf, binary.LittleEndian, d.iConfiguration) + binary.Write(buf, binary.LittleEndian, d.bmAttributes) + binary.Write(buf, binary.LittleEndian, d.bMaxPower) + return buf.Bytes() +} + +const interfaceDescriptorSize = 9 + +// InterfaceDescriptor implements the standard USB interface descriptor. +// +// Table 9-12. Standard Interface Descriptor +// bLength, bDescriptorType, bInterfaceNumber, bAlternateSetting, bNumEndpoints, bInterfaceClass, +// bInterfaceSubClass, bInterfaceProtocol, iInterface +// +type InterfaceDescriptor struct { + bLength uint8 // 9 + bDescriptorType uint8 // 4 + bInterfaceNumber uint8 + bAlternateSetting uint8 + bNumEndpoints uint8 + bInterfaceClass uint8 + bInterfaceSubClass uint8 + bInterfaceProtocol uint8 + iInterface uint8 +} + +// NewInterfaceDescriptor returns a new USB InterfaceDescriptor. +func NewInterfaceDescriptor(n, numEndpoints, class, subClass, protocol uint8) InterfaceDescriptor { + return InterfaceDescriptor{interfaceDescriptorSize, 4, n, 0, numEndpoints, class, subClass, protocol, 0} +} + +// Bytes returns InterfaceDescriptor data. +func (d InterfaceDescriptor) Bytes() []byte { + buf := bytes.NewBuffer(make([]byte, 0, interfaceDescriptorSize)) + binary.Write(buf, binary.LittleEndian, d.bLength) + binary.Write(buf, binary.LittleEndian, d.bDescriptorType) + binary.Write(buf, binary.LittleEndian, d.bInterfaceNumber) + binary.Write(buf, binary.LittleEndian, d.bAlternateSetting) + binary.Write(buf, binary.LittleEndian, d.bNumEndpoints) + binary.Write(buf, binary.LittleEndian, d.bInterfaceClass) + binary.Write(buf, binary.LittleEndian, d.bInterfaceSubClass) + binary.Write(buf, binary.LittleEndian, d.bInterfaceProtocol) + binary.Write(buf, binary.LittleEndian, d.iInterface) + return buf.Bytes() +} + +const endpointDescriptorSize = 7 + +// EndpointDescriptor implements the standard USB endpoint descriptor. +// +// Table 9-13. Standard Endpoint Descriptor +// bLength, bDescriptorType, bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval +// +type EndpointDescriptor struct { + bLength uint8 // 7 + bDescriptorType uint8 // 5 + bEndpointAddress uint8 + bmAttributes uint8 + wMaxPacketSize uint16 + bInterval uint8 +} + +// NewEndpointDescriptor returns a new USB EndpointDescriptor. +func NewEndpointDescriptor(addr, attr uint8, packetSize uint16, interval uint8) EndpointDescriptor { + return EndpointDescriptor{endpointDescriptorSize, 5, addr, attr, packetSize, interval} +} + +// Bytes returns EndpointDescriptor data. +func (d EndpointDescriptor) Bytes() []byte { + buf := bytes.NewBuffer(make([]byte, 0, endpointDescriptorSize)) + binary.Write(buf, binary.LittleEndian, d.bLength) + binary.Write(buf, binary.LittleEndian, d.bDescriptorType) + binary.Write(buf, binary.LittleEndian, d.bEndpointAddress) + binary.Write(buf, binary.LittleEndian, d.bmAttributes) + binary.Write(buf, binary.LittleEndian, d.wMaxPacketSize) + binary.Write(buf, binary.LittleEndian, d.bInterval) + return buf.Bytes() +} + +const iadDescriptorSize = 8 + +// IADDescriptor is an Interface Association Descriptor, which is used +// to bind 2 interfaces together in CDC composite device. +// +// Standard Interface Association Descriptor: +// bLength, bDescriptorType, bFirstInterface, bInterfaceCount, bFunctionClass, bFunctionSubClass, +// bFunctionProtocol, iFunction +// +type IADDescriptor struct { + bLength uint8 // 8 + bDescriptorType uint8 // 11 + bFirstInterface uint8 + bInterfaceCount uint8 + bFunctionClass uint8 + bFunctionSubClass uint8 + bFunctionProtocol uint8 + iFunction uint8 +} + +// NewIADDescriptor returns a new USB IADDescriptor. +func NewIADDescriptor(firstInterface, count, class, subClass, protocol uint8) IADDescriptor { + return IADDescriptor{iadDescriptorSize, 11, firstInterface, count, class, subClass, protocol, 0} +} + +// Bytes returns IADDescriptor data. +func (d IADDescriptor) Bytes() []byte { + buf := bytes.NewBuffer(make([]byte, 0, iadDescriptorSize)) + binary.Write(buf, binary.LittleEndian, d.bLength) + binary.Write(buf, binary.LittleEndian, d.bDescriptorType) + binary.Write(buf, binary.LittleEndian, d.bFirstInterface) + binary.Write(buf, binary.LittleEndian, d.bInterfaceCount) + binary.Write(buf, binary.LittleEndian, d.bFunctionClass) + binary.Write(buf, binary.LittleEndian, d.bFunctionSubClass) + binary.Write(buf, binary.LittleEndian, d.bFunctionProtocol) + binary.Write(buf, binary.LittleEndian, d.iFunction) + return buf.Bytes() +} + +const cdcCSInterfaceDescriptorSize = 5 + +// CDCCSInterfaceDescriptor is a CDC CS interface descriptor. +type CDCCSInterfaceDescriptor struct { + len uint8 // 5 + dtype uint8 // 0x24 + subtype uint8 + d0 uint8 + d1 uint8 +} + +// NewCDCCSInterfaceDescriptor returns a new USB CDCCSInterfaceDescriptor. +func NewCDCCSInterfaceDescriptor(subtype, d0, d1 uint8) CDCCSInterfaceDescriptor { + return CDCCSInterfaceDescriptor{cdcCSInterfaceDescriptorSize, 0x24, subtype, d0, d1} +} + +// Bytes returns CDCCSInterfaceDescriptor data. +func (d CDCCSInterfaceDescriptor) Bytes() []byte { + buf := bytes.NewBuffer(make([]byte, 0, cdcCSInterfaceDescriptorSize)) + binary.Write(buf, binary.LittleEndian, d.len) + binary.Write(buf, binary.LittleEndian, d.dtype) + binary.Write(buf, binary.LittleEndian, d.subtype) + binary.Write(buf, binary.LittleEndian, d.d0) + binary.Write(buf, binary.LittleEndian, d.d1) + return buf.Bytes() +} + +const cmFunctionalDescriptorSize = 5 + +// CMFunctionalDescriptor is the functional descriptor general format. +type CMFunctionalDescriptor struct { + bFunctionLength uint8 + bDescriptorType uint8 // 0x24 + bDescriptorSubtype uint8 // 1 + bmCapabilities uint8 + bDataInterface uint8 +} + +// NewCMFunctionalDescriptor returns a new USB CMFunctionalDescriptor. +func NewCMFunctionalDescriptor(subtype, d0, d1 uint8) CMFunctionalDescriptor { + return CMFunctionalDescriptor{5, 0x24, subtype, d0, d1} +} + +// Bytes returns the CMFunctionalDescriptor data. +func (d CMFunctionalDescriptor) Bytes() []byte { + buf := bytes.NewBuffer(make([]byte, 0, cmFunctionalDescriptorSize)) + binary.Write(buf, binary.LittleEndian, d.bFunctionLength) + binary.Write(buf, binary.LittleEndian, d.bDescriptorType) + binary.Write(buf, binary.LittleEndian, d.bDescriptorSubtype) + binary.Write(buf, binary.LittleEndian, d.bmCapabilities) + binary.Write(buf, binary.LittleEndian, d.bDataInterface) + return buf.Bytes() +} + +const acmFunctionalDescriptorSize = 4 + +// ACMFunctionalDescriptor is a Abstract Control Model (ACM) USB descriptor. +type ACMFunctionalDescriptor struct { + len uint8 + dtype uint8 // 0x24 + subtype uint8 // 1 + bmCapabilities uint8 +} + +// NewACMFunctionalDescriptor returns a new USB ACMFunctionalDescriptor. +func NewACMFunctionalDescriptor(subtype, d0 uint8) ACMFunctionalDescriptor { + return ACMFunctionalDescriptor{4, 0x24, subtype, d0} +} + +// Bytes returns the ACMFunctionalDescriptor data. +func (d ACMFunctionalDescriptor) Bytes() []byte { + buf := bytes.NewBuffer(make([]byte, 0, acmFunctionalDescriptorSize)) + binary.Write(buf, binary.LittleEndian, d.len) + binary.Write(buf, binary.LittleEndian, d.dtype) + binary.Write(buf, binary.LittleEndian, d.subtype) + binary.Write(buf, binary.LittleEndian, d.bmCapabilities) + return buf.Bytes() +} + +// CDCDescriptor is the Communication Device Class (CDC) descriptor. +type CDCDescriptor struct { + // IAD + iad IADDescriptor // Only needed on compound device + + // Control + cif InterfaceDescriptor + header CDCCSInterfaceDescriptor + + // CDC control + controlManagement ACMFunctionalDescriptor // ACM + functionalDescriptor CDCCSInterfaceDescriptor // CDC_UNION + callManagement CMFunctionalDescriptor // Call Management + cifin EndpointDescriptor + + // CDC Data + dif InterfaceDescriptor + in EndpointDescriptor + out EndpointDescriptor +} + +func NewCDCDescriptor(i IADDescriptor, c InterfaceDescriptor, + h CDCCSInterfaceDescriptor, + cm ACMFunctionalDescriptor, + fd CDCCSInterfaceDescriptor, + callm CMFunctionalDescriptor, + ci EndpointDescriptor, + di InterfaceDescriptor, + inp EndpointDescriptor, + outp EndpointDescriptor) CDCDescriptor { + return CDCDescriptor{iad: i, + cif: c, + header: h, + controlManagement: cm, + functionalDescriptor: fd, + callManagement: callm, + cifin: ci, + dif: di, + in: inp, + out: outp} +} + +const cdcSize = iadDescriptorSize + + interfaceDescriptorSize + + cdcCSInterfaceDescriptorSize + + acmFunctionalDescriptorSize + + cdcCSInterfaceDescriptorSize + + cmFunctionalDescriptorSize + + endpointDescriptorSize + + interfaceDescriptorSize + + endpointDescriptorSize + + endpointDescriptorSize + +// Bytes returns CDCDescriptor data. +func (d CDCDescriptor) Bytes() []byte { + buf := bytes.NewBuffer(make([]byte, 0, cdcSize)) + buf.Write(d.iad.Bytes()) + buf.Write(d.cif.Bytes()) + buf.Write(d.header.Bytes()) + buf.Write(d.controlManagement.Bytes()) + buf.Write(d.functionalDescriptor.Bytes()) + buf.Write(d.callManagement.Bytes()) + buf.Write(d.cifin.Bytes()) + buf.Write(d.dif.Bytes()) + buf.Write(d.in.Bytes()) + buf.Write(d.out.Bytes()) + return buf.Bytes() +} + +// MSCDescriptor is not used yet. +type MSCDescriptor struct { + msc InterfaceDescriptor + in EndpointDescriptor + out EndpointDescriptor +} + +type cdcLineInfo struct { + dwDTERate uint32 + bCharFormat uint8 + bParityType uint8 + bDataBits uint8 + lineState uint8 +} + +var ( + // TODO: allow setting these + usb_STRING_LANGUAGE = [2]uint16{(3 << 8) | (2 + 2), 0x0409} // English + usb_STRING_PRODUCT = "Arduino Zero" + usb_STRING_MANUFACTURER = "Arduino" + + usb_VID uint16 = 0x2341 + usb_PID uint16 = 0x004d +) + +const ( + usb_IMANUFACTURER = 1 + usb_IPRODUCT = 2 + usb_ISERIAL = 3 + + usb_ENDPOINT_TYPE_CONTROL = 0x00 + usb_ENDPOINT_TYPE_ISOCHRONOUS = 0x01 + usb_ENDPOINT_TYPE_BULK = 0x02 + usb_ENDPOINT_TYPE_INTERRUPT = 0x03 + + usb_DEVICE_DESCRIPTOR_TYPE = 1 + usb_CONFIGURATION_DESCRIPTOR_TYPE = 2 + usb_STRING_DESCRIPTOR_TYPE = 3 + usb_INTERFACE_DESCRIPTOR_TYPE = 4 + usb_ENDPOINT_DESCRIPTOR_TYPE = 5 + usb_DEVICE_QUALIFIER = 6 + usb_OTHER_SPEED_CONFIGURATION = 7 + + usbEndpointOut = 0x00 + usbEndpointIn = 0x80 + + usbEndpointPacketSize = 64 // 64 for Full Speed, EPT size max is 1024 + usb_EPT_NUM = 7 + + // standard requests + usb_GET_STATUS = 0 + usb_CLEAR_FEATURE = 1 + usb_SET_FEATURE = 3 + usb_SET_ADDRESS = 5 + usb_GET_DESCRIPTOR = 6 + usb_SET_DESCRIPTOR = 7 + usb_GET_CONFIGURATION = 8 + usb_SET_CONFIGURATION = 9 + usb_GET_INTERFACE = 10 + usb_SET_INTERFACE = 11 + + usb_DEVICE_CLASS_COMMUNICATIONS = 0x02 + usb_DEVICE_CLASS_HUMAN_INTERFACE = 0x03 + usb_DEVICE_CLASS_STORAGE = 0x08 + usb_DEVICE_CLASS_VENDOR_SPECIFIC = 0xFF + + usb_CONFIG_POWERED_MASK = 0x40 + usb_CONFIG_BUS_POWERED = 0x80 + usb_CONFIG_SELF_POWERED = 0xC0 + usb_CONFIG_REMOTE_WAKEUP = 0x20 + + // CDC + usb_CDC_ACM_INTERFACE = 0 // CDC ACM + usb_CDC_DATA_INTERFACE = 1 // CDC Data + usb_CDC_FIRST_ENDPOINT = 1 + usb_CDC_ENDPOINT_ACM = 1 + usb_CDC_ENDPOINT_OUT = 2 + usb_CDC_ENDPOINT_IN = 3 + + // 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 +) + +// usbDeviceDescBank is the USB device endpoint descriptor. +// typedef struct { +// __IO USB_DEVICE_ADDR_Type ADDR; /**< \brief Offset: 0x000 (R/W 32) DEVICE_DESC_BANK Endpoint Bank, Adress of Data Buffer */ +// __IO USB_DEVICE_PCKSIZE_Type PCKSIZE; /**< \brief Offset: 0x004 (R/W 32) DEVICE_DESC_BANK Endpoint Bank, Packet Size */ +// __IO USB_DEVICE_EXTREG_Type EXTREG; /**< \brief Offset: 0x008 (R/W 16) DEVICE_DESC_BANK Endpoint Bank, Extended */ +// __IO USB_DEVICE_STATUS_BK_Type STATUS_BK; /**< \brief Offset: 0x00A (R/W 8) DEVICE_DESC_BANK Enpoint Bank, Status of Bank */ +// RoReg8 Reserved1[0x5]; +// } UsbDeviceDescBank; +type usbDeviceDescBank struct { + ADDR sam.RegValue + PCKSIZE sam.RegValue + EXTREG sam.RegValue16 + STATUS_BK sam.RegValue8 + _reserved [5]sam.RegValue8 +} + +type usbDeviceDescriptor struct { + DeviceDescBank [2]usbDeviceDescBank +} + +// typedef struct { +// union { +// uint8_t bmRequestType; +// struct { +// uint8_t direction : 5; +// uint8_t type : 2; +// uint8_t transferDirection : 1; +// }; +// }; +// uint8_t bRequest; +// uint8_t wValueL; +// uint8_t wValueH; +// uint16_t wIndex; +// uint16_t wLength; +// } USBSetup; +type usbSetup struct { + bmRequestType uint8 + bRequest uint8 + wValueL uint8 + wValueH uint8 + wIndex uint16 + wLength uint16 +} + +func newUSBSetup(data []byte) usbSetup { + buf := bytes.NewBuffer(data) + u := usbSetup{} + binary.Read(buf, binary.LittleEndian, &(u.bmRequestType)) + binary.Read(buf, binary.LittleEndian, &(u.bRequest)) + binary.Read(buf, binary.LittleEndian, &(u.wValueL)) + binary.Read(buf, binary.LittleEndian, &(u.wValueH)) + binary.Read(buf, binary.LittleEndian, &(u.wIndex)) + binary.Read(buf, binary.LittleEndian, &(u.wLength)) + 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, errors.New("Buffer empty") + } + 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) +} diff --git a/src/runtime/runtime_atsamd21.go b/src/runtime/runtime_atsamd21.go index 09a093cdd..23e13ad42 100644 --- a/src/runtime/runtime_atsamd21.go +++ b/src/runtime/runtime_atsamd21.go @@ -24,8 +24,9 @@ func init() { initRTC() initUARTClock() initI2CClock() + initUSBClock() - // connect to UART + // connect to USB CDC interface machine.UART0.Configure(machine.UARTConfig{}) } @@ -331,3 +332,14 @@ func initI2CClock() { sam.GCLK_CLKCTRL_CLKEN) waitForSync() } + +func initUSBClock() { + // Turn on clock for USB + sam.PM.APBBMASK |= sam.PM_APBBMASK_USB_ + + // Put Generic Clock Generator 0 as source for Generic Clock Multiplexer 6 (USB reference) + sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_USB << sam.GCLK_CLKCTRL_ID_Pos) | + (sam.GCLK_CLKCTRL_GEN_GCLK0 << sam.GCLK_CLKCTRL_GEN_Pos) | + sam.GCLK_CLKCTRL_CLKEN) + waitForSync() +} |