417 строки
		
	
	
	
		
			10 КиБ
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			417 строки
		
	
	
	
		
			10 КиБ
		
	
	
	
		
			Go
		
	
	
	
	
	
| //go:build sam || nrf52840
 | |
| // +build sam nrf52840
 | |
| 
 | |
| package machine
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"runtime/volatile"
 | |
| )
 | |
| 
 | |
| type USBDevice struct {
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	USBDev = &USBDevice{}
 | |
| )
 | |
| var usbDescriptor = descriptorCDC
 | |
| 
 | |
| var (
 | |
| 	errUSBCDCBufferEmpty      = errors.New("USB-CDC buffer empty")
 | |
| 	errUSBCDCWriteByteTimeout = errors.New("USB-CDC write byte timeout")
 | |
| 	errUSBCDCReadTimeout      = errors.New("USB-CDC read timeout")
 | |
| 	errUSBCDCBytesRead        = errors.New("USB-CDC invalid number of bytes read")
 | |
| )
 | |
| 
 | |
| const cdcLineInfoSize = 7
 | |
| 
 | |
| type cdcLineInfo struct {
 | |
| 	dwDTERate   uint32
 | |
| 	bCharFormat uint8
 | |
| 	bParityType uint8
 | |
| 	bDataBits   uint8
 | |
| 	lineState   uint8
 | |
| }
 | |
| 
 | |
| // strToUTF16LEDescriptor converts a utf8 string into a string descriptor
 | |
| // note: the following code only converts ascii characters to UTF16LE. In order
 | |
| // to do a "proper" conversion, we would need to pull in the 'unicode/utf16'
 | |
| // package, which at the time this was written added 512 bytes to the compiled
 | |
| // binary.
 | |
| func strToUTF16LEDescriptor(in string, out []byte) {
 | |
| 	out[0] = byte(len(out))
 | |
| 	out[1] = usb_STRING_DESCRIPTOR_TYPE
 | |
| 	for i, rune := range in {
 | |
| 		out[(i<<1)+2] = byte(rune)
 | |
| 		out[(i<<1)+3] = 0
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	// TODO: allow setting these
 | |
| 	usb_STRING_LANGUAGE = [2]uint16{(3 << 8) | (2 + 2), 0x0409} // English
 | |
| )
 | |
| 
 | |
| 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}
 | |
| )
 | |
| 
 | |
| 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
 | |
| 	usb_SET_REPORT_TYPE               = 33
 | |
| 	usb_HID_REPORT_TYPE               = 34
 | |
| 
 | |
| 	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
 | |
| 
 | |
| 	// non standard requests
 | |
| 	usb_SET_IDLE = 10
 | |
| 
 | |
| 	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
 | |
| 
 | |
| 	// Endpoint
 | |
| 	usb_CDC_ENDPOINT_ACM = 1
 | |
| 	usb_CDC_ENDPOINT_OUT = 2
 | |
| 	usb_CDC_ENDPOINT_IN  = 3
 | |
| 	usb_HID_ENDPOINT_IN  = 4
 | |
| 
 | |
| 	// 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
 | |
| 
 | |
| 	usb_CDC_LINESTATE_DTR = 0x01
 | |
| 	usb_CDC_LINESTATE_RTS = 0x02
 | |
| )
 | |
| 
 | |
| // usbDeviceDescBank is the USB device endpoint descriptor.
 | |
| type usbDeviceDescBank struct {
 | |
| 	ADDR      volatile.Register32
 | |
| 	PCKSIZE   volatile.Register32
 | |
| 	EXTREG    volatile.Register16
 | |
| 	STATUS_BK volatile.Register8
 | |
| 	_reserved [5]volatile.Register8
 | |
| }
 | |
| 
 | |
| type usbDeviceDescriptor struct {
 | |
| 	DeviceDescBank [2]usbDeviceDescBank
 | |
| }
 | |
| 
 | |
| type USBSetup struct {
 | |
| 	BmRequestType uint8
 | |
| 	BRequest      uint8
 | |
| 	WValueL       uint8
 | |
| 	WValueH       uint8
 | |
| 	WIndex        uint16
 | |
| 	WLength       uint16
 | |
| }
 | |
| 
 | |
| func newUSBSetup(data []byte) USBSetup {
 | |
| 	u := USBSetup{}
 | |
| 	u.BmRequestType = uint8(data[0])
 | |
| 	u.BRequest = uint8(data[1])
 | |
| 	u.WValueL = uint8(data[2])
 | |
| 	u.WValueH = uint8(data[3])
 | |
| 	u.WIndex = uint16(data[4]) | (uint16(data[5]) << 8)
 | |
| 	u.WLength = uint16(data[6]) | (uint16(data[7]) << 8)
 | |
| 	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, errUSBCDCBufferEmpty
 | |
| 	}
 | |
| 	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)
 | |
| }
 | |
| 
 | |
| // 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:
 | |
| 		sendUSBPacket(0, usbDescriptor.Configuration, setup.WLength)
 | |
| 		return
 | |
| 	case usb_DEVICE_DESCRIPTOR_TYPE:
 | |
| 		// composite descriptor
 | |
| 		usbDescriptor.Configure(usb_VID, usb_PID)
 | |
| 		sendUSBPacket(0, usbDescriptor.Device, setup.WLength)
 | |
| 		return
 | |
| 
 | |
| 	case usb_STRING_DESCRIPTOR_TYPE:
 | |
| 		switch setup.WValueL {
 | |
| 		case 0:
 | |
| 			b := []byte{0x04, 0x03, 0x09, 0x04}
 | |
| 			sendUSBPacket(0, b, setup.WLength)
 | |
| 
 | |
| 		case usb_IPRODUCT:
 | |
| 			b := make([]byte, (len(usb_STRING_PRODUCT)<<1)+2)
 | |
| 			strToUTF16LEDescriptor(usb_STRING_PRODUCT, b)
 | |
| 			sendUSBPacket(0, b, setup.WLength)
 | |
| 
 | |
| 		case usb_IMANUFACTURER:
 | |
| 			b := make([]byte, (len(usb_STRING_MANUFACTURER)<<1)+2)
 | |
| 			strToUTF16LEDescriptor(usb_STRING_MANUFACTURER, b)
 | |
| 			sendUSBPacket(0, b, setup.WLength)
 | |
| 
 | |
| 		case usb_ISERIAL:
 | |
| 			// TODO: allow returning a product serial number
 | |
| 			SendZlp()
 | |
| 		}
 | |
| 		return
 | |
| 	case usb_HID_REPORT_TYPE:
 | |
| 		if h, ok := usbDescriptor.HID[setup.WIndex]; ok {
 | |
| 			sendUSBPacket(0, h, setup.WLength)
 | |
| 			return
 | |
| 		}
 | |
| 	case usb_DEVICE_QUALIFIER:
 | |
| 		// skip
 | |
| 	default:
 | |
| 	}
 | |
| 
 | |
| 	// do not know how to handle this message, so return zero
 | |
| 	SendZlp()
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func handleStandardSetup(setup USBSetup) bool {
 | |
| 	switch setup.BRequest {
 | |
| 	case usb_GET_STATUS:
 | |
| 		buf := []byte{0, 0}
 | |
| 
 | |
| 		if setup.BmRequestType != 0 { // endpoint
 | |
| 			if isEndpointHalt {
 | |
| 				buf[0] = 1
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		sendUSBPacket(0, buf, setup.WLength)
 | |
| 		return true
 | |
| 
 | |
| 	case usb_CLEAR_FEATURE:
 | |
| 		if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
 | |
| 			isRemoteWakeUpEnabled = false
 | |
| 		} else if setup.WValueL == 0 { // ENDPOINTHALT
 | |
| 			isEndpointHalt = false
 | |
| 		}
 | |
| 		SendZlp()
 | |
| 		return true
 | |
| 
 | |
| 	case usb_SET_FEATURE:
 | |
| 		if setup.WValueL == 1 { // DEVICEREMOTEWAKEUP
 | |
| 			isRemoteWakeUpEnabled = true
 | |
| 		} else if setup.WValueL == 0 { // ENDPOINTHALT
 | |
| 			isEndpointHalt = true
 | |
| 		}
 | |
| 		SendZlp()
 | |
| 		return true
 | |
| 
 | |
| 	case usb_SET_ADDRESS:
 | |
| 		return handleUSBSetAddress(setup)
 | |
| 
 | |
| 	case usb_GET_DESCRIPTOR:
 | |
| 		sendDescriptor(setup)
 | |
| 		return true
 | |
| 
 | |
| 	case usb_SET_DESCRIPTOR:
 | |
| 		return false
 | |
| 
 | |
| 	case usb_GET_CONFIGURATION:
 | |
| 		buff := []byte{usbConfiguration}
 | |
| 		sendUSBPacket(0, buff, setup.WLength)
 | |
| 		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
 | |
| 
 | |
| 			SendZlp()
 | |
| 			return true
 | |
| 		} else {
 | |
| 			return false
 | |
| 		}
 | |
| 
 | |
| 	case usb_GET_INTERFACE:
 | |
| 		buff := []byte{usbSetInterface}
 | |
| 		sendUSBPacket(0, buff, setup.WLength)
 | |
| 		return true
 | |
| 
 | |
| 	case usb_SET_INTERFACE:
 | |
| 		usbSetInterface = setup.WValueL
 | |
| 
 | |
| 		SendZlp()
 | |
| 		return true
 | |
| 
 | |
| 	default:
 | |
| 		return true
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // EnableHID enables HID. This function must be executed from the init().
 | |
| func EnableHID(callback func()) {
 | |
| 	usbDescriptor = descriptorCDCHID
 | |
| 	endPoints = []uint32{usb_ENDPOINT_TYPE_CONTROL,
 | |
| 		(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn),
 | |
| 		(usb_ENDPOINT_TYPE_BULK | usbEndpointOut),
 | |
| 		(usb_ENDPOINT_TYPE_BULK | usbEndpointIn),
 | |
| 		(usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn)}
 | |
| 
 | |
| 	hidCallback = callback
 | |
| }
 | |
| 
 | |
| // hidCallback is a variable that holds the callback when using HID.
 | |
| var hidCallback func()
 | 
