aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRon Evans <[email protected]>2019-02-21 20:29:12 +0100
committerAyke van Laethem <[email protected]>2019-02-23 13:34:00 +0100
commit5438f16fcb1b541715b0717d79d78fb75123a18a (patch)
tree0ad644a6c1e5c3745f04d36d39dcbb128760a41b
parent7f027ddd330a0851faf917ac4d6c71f043348f6b (diff)
downloadtinygo-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.go8
-rw-r--r--src/machine/machine_atsamd21.go933
-rw-r--r--src/machine/usb.go599
-rw-r--r--src/runtime/runtime_atsamd21.go14
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()
+}