github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/usb.go (about)

     1  //go:build sam || nrf52840 || rp2040
     2  
     3  package machine
     4  
     5  import (
     6  	"machine/usb"
     7  	"machine/usb/descriptor"
     8  
     9  	"errors"
    10  )
    11  
    12  type USBDevice struct {
    13  	initcomplete         bool
    14  	InitEndpointComplete bool
    15  }
    16  
    17  var (
    18  	USBDev = &USBDevice{}
    19  	USBCDC Serialer
    20  )
    21  
    22  type Serialer interface {
    23  	WriteByte(c byte) error
    24  	Write(data []byte) (n int, err error)
    25  	Configure(config UARTConfig) error
    26  	Buffered() int
    27  	ReadByte() (byte, error)
    28  	DTR() bool
    29  	RTS() bool
    30  }
    31  
    32  var usbDescriptor descriptor.Descriptor
    33  
    34  func usbVendorID() uint16 {
    35  	if usb.VendorID != 0 {
    36  		return usb.VendorID
    37  	}
    38  
    39  	return usb_VID
    40  }
    41  
    42  func usbProductID() uint16 {
    43  	if usb.ProductID != 0 {
    44  		return usb.ProductID
    45  	}
    46  
    47  	return usb_PID
    48  }
    49  
    50  func usbManufacturer() string {
    51  	if usb.Manufacturer != "" {
    52  		return usb.Manufacturer
    53  	}
    54  
    55  	return usb_STRING_MANUFACTURER
    56  }
    57  
    58  func usbProduct() string {
    59  	if usb.Product != "" {
    60  		return usb.Product
    61  	}
    62  
    63  	return usb_STRING_PRODUCT
    64  }
    65  
    66  func usbSerial() string {
    67  	if usb.Serial != "" {
    68  		return usb.Serial
    69  	}
    70  	return ""
    71  }
    72  
    73  // strToUTF16LEDescriptor converts a utf8 string into a string descriptor
    74  // note: the following code only converts ascii characters to UTF16LE. In order
    75  // to do a "proper" conversion, we would need to pull in the 'unicode/utf16'
    76  // package, which at the time this was written added 512 bytes to the compiled
    77  // binary.
    78  func strToUTF16LEDescriptor(in string, out []byte) {
    79  	out[0] = byte(len(out))
    80  	out[1] = descriptor.TypeString
    81  	for i, rune := range in {
    82  		out[(i<<1)+2] = byte(rune)
    83  		out[(i<<1)+3] = 0
    84  	}
    85  	return
    86  }
    87  
    88  const cdcLineInfoSize = 7
    89  
    90  var (
    91  	ErrUSBReadTimeout  = errors.New("USB read timeout")
    92  	ErrUSBBytesRead    = errors.New("USB invalid number of bytes read")
    93  	ErrUSBBytesWritten = errors.New("USB invalid number of bytes written")
    94  )
    95  
    96  var (
    97  	usbEndpointDescriptors [usb.NumberOfEndpoints]descriptor.Device
    98  
    99  	isEndpointHalt        = false
   100  	isRemoteWakeUpEnabled = false
   101  
   102  	usbConfiguration uint8
   103  	usbSetInterface  uint8
   104  )
   105  
   106  //go:align 4
   107  var udd_ep_control_cache_buffer [256]uint8
   108  
   109  //go:align 4
   110  var udd_ep_in_cache_buffer [usb.NumberOfEndpoints][64]uint8
   111  
   112  //go:align 4
   113  var udd_ep_out_cache_buffer [usb.NumberOfEndpoints][64]uint8
   114  
   115  // usb_trans_buffer max size is 255 since that is max size
   116  // for a descriptor (bLength is 1 byte), and the biggest use
   117  // for this buffer is to transmit string descriptors.  If
   118  // this buffer is used for new purposes in future the length
   119  // must be revisited.
   120  var usb_trans_buffer [255]uint8
   121  
   122  var (
   123  	usbTxHandler    [usb.NumberOfEndpoints]func()
   124  	usbRxHandler    [usb.NumberOfEndpoints]func([]byte)
   125  	usbSetupHandler [usb.NumberOfInterfaces]func(usb.Setup) bool
   126  
   127  	endPoints = []uint32{
   128  		usb.CONTROL_ENDPOINT:  usb.ENDPOINT_TYPE_CONTROL,
   129  		usb.CDC_ENDPOINT_ACM:  (usb.ENDPOINT_TYPE_INTERRUPT | usb.EndpointIn),
   130  		usb.CDC_ENDPOINT_OUT:  (usb.ENDPOINT_TYPE_BULK | usb.EndpointOut),
   131  		usb.CDC_ENDPOINT_IN:   (usb.ENDPOINT_TYPE_BULK | usb.EndpointIn),
   132  		usb.HID_ENDPOINT_IN:   (usb.ENDPOINT_TYPE_DISABLE), // Interrupt In
   133  		usb.HID_ENDPOINT_OUT:  (usb.ENDPOINT_TYPE_DISABLE), // Interrupt Out
   134  		usb.MIDI_ENDPOINT_IN:  (usb.ENDPOINT_TYPE_DISABLE), // Bulk In
   135  		usb.MIDI_ENDPOINT_OUT: (usb.ENDPOINT_TYPE_DISABLE), // Bulk Out
   136  	}
   137  )
   138  
   139  // sendDescriptor creates and sends the various USB descriptor types that
   140  // can be requested by the host.
   141  func sendDescriptor(setup usb.Setup) {
   142  	switch setup.WValueH {
   143  	case descriptor.TypeConfiguration:
   144  		sendUSBPacket(0, usbDescriptor.Configuration, setup.WLength)
   145  		return
   146  	case descriptor.TypeDevice:
   147  		usbDescriptor.Configure(usbVendorID(), usbProductID())
   148  		sendUSBPacket(0, usbDescriptor.Device, setup.WLength)
   149  		return
   150  
   151  	case descriptor.TypeString:
   152  		switch setup.WValueL {
   153  		case 0:
   154  			usb_trans_buffer[0] = 0x04
   155  			usb_trans_buffer[1] = 0x03
   156  			usb_trans_buffer[2] = 0x09
   157  			usb_trans_buffer[3] = 0x04
   158  			sendUSBPacket(0, usb_trans_buffer[:4], setup.WLength)
   159  
   160  		case usb.IPRODUCT:
   161  			b := usb_trans_buffer[:(len(usbProduct())<<1)+2]
   162  			strToUTF16LEDescriptor(usbProduct(), b)
   163  			sendUSBPacket(0, b, setup.WLength)
   164  
   165  		case usb.IMANUFACTURER:
   166  			b := usb_trans_buffer[:(len(usbManufacturer())<<1)+2]
   167  			strToUTF16LEDescriptor(usbManufacturer(), b)
   168  			sendUSBPacket(0, b, setup.WLength)
   169  
   170  		case usb.ISERIAL:
   171  			sz := len(usbSerial())
   172  			if sz == 0 {
   173  				SendZlp()
   174  			} else {
   175  				b := usb_trans_buffer[:(sz<<1)+2]
   176  				strToUTF16LEDescriptor(usbSerial(), b)
   177  				sendUSBPacket(0, b, setup.WLength)
   178  			}
   179  		}
   180  		return
   181  	case descriptor.TypeHIDReport:
   182  		if h, ok := usbDescriptor.HID[setup.WIndex]; ok {
   183  			sendUSBPacket(0, h, setup.WLength)
   184  			return
   185  		}
   186  	case descriptor.TypeDeviceQualifier:
   187  		// skip
   188  	default:
   189  	}
   190  
   191  	// do not know how to handle this message, so return zero
   192  	SendZlp()
   193  	return
   194  }
   195  
   196  func handleStandardSetup(setup usb.Setup) bool {
   197  	switch setup.BRequest {
   198  	case usb.GET_STATUS:
   199  		usb_trans_buffer[0] = 0
   200  		usb_trans_buffer[1] = 0
   201  
   202  		if setup.BmRequestType != 0 { // endpoint
   203  			if isEndpointHalt {
   204  				usb_trans_buffer[0] = 1
   205  			}
   206  		}
   207  
   208  		sendUSBPacket(0, usb_trans_buffer[:2], setup.WLength)
   209  		return true
   210  
   211  	case usb.CLEAR_FEATURE:
   212  		if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
   213  			isRemoteWakeUpEnabled = false
   214  		} else if setup.WValueL == 0 { // ENDPOINTHALT
   215  			isEndpointHalt = false
   216  		}
   217  		SendZlp()
   218  		return true
   219  
   220  	case usb.SET_FEATURE:
   221  		if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
   222  			isRemoteWakeUpEnabled = true
   223  		} else if setup.WValueL == 0 { // ENDPOINTHALT
   224  			isEndpointHalt = true
   225  		}
   226  		SendZlp()
   227  		return true
   228  
   229  	case usb.SET_ADDRESS:
   230  		return handleUSBSetAddress(setup)
   231  
   232  	case usb.GET_DESCRIPTOR:
   233  		sendDescriptor(setup)
   234  		return true
   235  
   236  	case usb.SET_DESCRIPTOR:
   237  		return false
   238  
   239  	case usb.GET_CONFIGURATION:
   240  		usb_trans_buffer[0] = usbConfiguration
   241  		sendUSBPacket(0, usb_trans_buffer[:1], setup.WLength)
   242  		return true
   243  
   244  	case usb.SET_CONFIGURATION:
   245  		if setup.BmRequestType&usb.REQUEST_RECIPIENT == usb.REQUEST_DEVICE {
   246  			for i := 1; i < len(endPoints); i++ {
   247  				initEndpoint(uint32(i), endPoints[i])
   248  			}
   249  
   250  			usbConfiguration = setup.WValueL
   251  			USBDev.InitEndpointComplete = true
   252  
   253  			SendZlp()
   254  			return true
   255  		} else {
   256  			return false
   257  		}
   258  
   259  	case usb.GET_INTERFACE:
   260  		usb_trans_buffer[0] = usbSetInterface
   261  		sendUSBPacket(0, usb_trans_buffer[:1], setup.WLength)
   262  		return true
   263  
   264  	case usb.SET_INTERFACE:
   265  		usbSetInterface = setup.WValueL
   266  
   267  		SendZlp()
   268  		return true
   269  
   270  	default:
   271  		return true
   272  	}
   273  }
   274  
   275  func EnableCDC(txHandler func(), rxHandler func([]byte), setupHandler func(usb.Setup) bool) {
   276  	if len(usbDescriptor.Device) == 0 {
   277  		usbDescriptor = descriptor.CDC
   278  	}
   279  	// Initialization of endpoints is required even for non-CDC
   280  	ConfigureUSBEndpoint(usbDescriptor,
   281  		[]usb.EndpointConfig{
   282  			{
   283  				Index: usb.CDC_ENDPOINT_ACM,
   284  				IsIn:  true,
   285  				Type:  usb.ENDPOINT_TYPE_INTERRUPT,
   286  			},
   287  			{
   288  				Index:     usb.CDC_ENDPOINT_OUT,
   289  				IsIn:      false,
   290  				Type:      usb.ENDPOINT_TYPE_BULK,
   291  				RxHandler: rxHandler,
   292  			},
   293  			{
   294  				Index:     usb.CDC_ENDPOINT_IN,
   295  				IsIn:      true,
   296  				Type:      usb.ENDPOINT_TYPE_BULK,
   297  				TxHandler: txHandler,
   298  			},
   299  		},
   300  		[]usb.SetupConfig{
   301  			{
   302  				Index:   usb.CDC_ACM_INTERFACE,
   303  				Handler: setupHandler,
   304  			},
   305  		})
   306  }
   307  
   308  func ConfigureUSBEndpoint(desc descriptor.Descriptor, epSettings []usb.EndpointConfig, setup []usb.SetupConfig) {
   309  	usbDescriptor = desc
   310  
   311  	for _, ep := range epSettings {
   312  		if ep.IsIn {
   313  			endPoints[ep.Index] = uint32(ep.Type | usb.EndpointIn)
   314  			if ep.TxHandler != nil {
   315  				usbTxHandler[ep.Index] = ep.TxHandler
   316  			}
   317  		} else {
   318  			endPoints[ep.Index] = uint32(ep.Type | usb.EndpointOut)
   319  			if ep.RxHandler != nil {
   320  				usbRxHandler[ep.Index] = ep.RxHandler
   321  			}
   322  		}
   323  	}
   324  
   325  	for _, s := range setup {
   326  		usbSetupHandler[s.Index] = s.Handler
   327  	}
   328  }