github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/usb/cdc/usbcdc.go (about) 1 package cdc 2 3 import ( 4 "errors" 5 "machine" 6 "machine/usb" 7 "runtime/interrupt" 8 ) 9 10 var ( 11 ErrBufferEmpty = errors.New("USB-CDC buffer empty") 12 ) 13 14 const cdcLineInfoSize = 7 15 16 type cdcLineInfo struct { 17 dwDTERate uint32 18 bCharFormat uint8 19 bParityType uint8 20 bDataBits uint8 21 lineState uint8 22 } 23 24 // Read from the RX buffer. 25 func (usbcdc *USBCDC) Read(data []byte) (n int, err error) { 26 // check if RX buffer is empty 27 size := usbcdc.Buffered() 28 if size == 0 { 29 return 0, nil 30 } 31 32 // Make sure we do not read more from buffer than the data slice can hold. 33 if len(data) < size { 34 size = len(data) 35 } 36 37 // only read number of bytes used from buffer 38 for i := 0; i < size; i++ { 39 v, _ := usbcdc.ReadByte() 40 data[i] = v 41 } 42 43 return size, nil 44 } 45 46 // ReadByte reads a single byte from the RX buffer. 47 // If there is no data in the buffer, returns an error. 48 func (usbcdc *USBCDC) ReadByte() (byte, error) { 49 // check if RX buffer is empty 50 buf, ok := usbcdc.rxBuffer.Get() 51 if !ok { 52 return 0, ErrBufferEmpty 53 } 54 return buf, nil 55 } 56 57 // Buffered returns the number of bytes currently stored in the RX buffer. 58 func (usbcdc *USBCDC) Buffered() int { 59 return int(usbcdc.rxBuffer.Used()) 60 } 61 62 // Receive handles adding data to the UART's data buffer. 63 // Usually called by the IRQ handler for a machine. 64 func (usbcdc *USBCDC) Receive(data byte) { 65 usbcdc.rxBuffer.Put(data) 66 } 67 68 // USBCDC is the USB CDC aka serial over USB interface. 69 type USBCDC struct { 70 rxBuffer *rxRingBuffer 71 txBuffer *txRingBuffer 72 waitTxc bool 73 } 74 75 var ( 76 // USB is a USB CDC interface. 77 USB *USBCDC 78 79 usbLineInfo = cdcLineInfo{115200, 0x00, 0x00, 0x08, 0x00} 80 ) 81 82 // Configure the USB CDC interface. The config is here for compatibility with the UART interface. 83 func (usbcdc *USBCDC) Configure(config machine.UARTConfig) error { 84 return nil 85 } 86 87 // Flush flushes buffered data. 88 func (usbcdc *USBCDC) Flush() { 89 mask := interrupt.Disable() 90 if b, ok := usbcdc.txBuffer.Get(); ok { 91 machine.SendUSBInPacket(cdcEndpointIn, b) 92 } else { 93 usbcdc.waitTxc = false 94 } 95 interrupt.Restore(mask) 96 } 97 98 // Write data to the USBCDC. 99 func (usbcdc *USBCDC) Write(data []byte) (n int, err error) { 100 if usbLineInfo.lineState > 0 { 101 mask := interrupt.Disable() 102 usbcdc.txBuffer.Put(data) 103 if !usbcdc.waitTxc { 104 usbcdc.waitTxc = true 105 usbcdc.Flush() 106 } 107 interrupt.Restore(mask) 108 } 109 return len(data), nil 110 } 111 112 // WriteByte writes a byte of data to the USB CDC interface. 113 func (usbcdc *USBCDC) WriteByte(c byte) error { 114 usbcdc.Write([]byte{c}) 115 return nil 116 } 117 118 func (usbcdc *USBCDC) DTR() bool { 119 return (usbLineInfo.lineState & usb_CDC_LINESTATE_DTR) > 0 120 } 121 122 func (usbcdc *USBCDC) RTS() bool { 123 return (usbLineInfo.lineState & usb_CDC_LINESTATE_RTS) > 0 124 } 125 126 func cdcCallbackRx(b []byte) { 127 for i := range b { 128 USB.Receive(b[i]) 129 } 130 } 131 132 var cdcSetupBuff [cdcLineInfoSize]byte 133 134 func cdcSetup(setup usb.Setup) bool { 135 if setup.BmRequestType == usb_REQUEST_DEVICETOHOST_CLASS_INTERFACE { 136 if setup.BRequest == usb_CDC_GET_LINE_CODING { 137 cdcSetupBuff[0] = byte(usbLineInfo.dwDTERate) 138 cdcSetupBuff[1] = byte(usbLineInfo.dwDTERate >> 8) 139 cdcSetupBuff[2] = byte(usbLineInfo.dwDTERate >> 16) 140 cdcSetupBuff[3] = byte(usbLineInfo.dwDTERate >> 24) 141 cdcSetupBuff[4] = byte(usbLineInfo.bCharFormat) 142 cdcSetupBuff[5] = byte(usbLineInfo.bParityType) 143 cdcSetupBuff[6] = byte(usbLineInfo.bDataBits) 144 145 machine.SendUSBInPacket(0, cdcSetupBuff[:]) 146 return true 147 } 148 } 149 150 if setup.BmRequestType == usb_REQUEST_HOSTTODEVICE_CLASS_INTERFACE { 151 if setup.BRequest == usb_CDC_SET_LINE_CODING { 152 b, err := machine.ReceiveUSBControlPacket() 153 if err != nil { 154 return false 155 } 156 157 usbLineInfo.dwDTERate = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 158 usbLineInfo.bCharFormat = b[4] 159 usbLineInfo.bParityType = b[5] 160 usbLineInfo.bDataBits = b[6] 161 } 162 163 if setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE { 164 usbLineInfo.lineState = setup.WValueL 165 } 166 167 if setup.BRequest == usb_CDC_SET_LINE_CODING || setup.BRequest == usb_CDC_SET_CONTROL_LINE_STATE { 168 // auto-reset into the bootloader 169 if usbLineInfo.dwDTERate == 1200 && usbLineInfo.lineState&usb_CDC_LINESTATE_DTR == 0 { 170 machine.EnterBootloader() 171 } else { 172 // TODO: cancel any reset 173 } 174 machine.SendZlp() 175 } 176 177 if setup.BRequest == usb_CDC_SEND_BREAK { 178 // TODO: something with this value? 179 // breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL; 180 // return false; 181 machine.SendZlp() 182 } 183 return true 184 } 185 return false 186 } 187 188 func EnableUSBCDC() { 189 machine.USBCDC = New() 190 machine.EnableCDC(USB.Flush, cdcCallbackRx, cdcSetup) 191 }