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  }