github.com/usbarmory/tamago@v0.0.0-20240508072735-8612bbe1e454/soc/nxp/usb/setup.go (about)

     1  // USB device mode support
     2  // https://github.com/usbarmory/tamago
     3  //
     4  // Copyright (c) WithSecure Corporation
     5  // https://foundry.withsecure.com
     6  //
     7  // Use of this source code is governed by the license
     8  // that can be found in the LICENSE file.
     9  
    10  package usb
    11  
    12  import (
    13  	"encoding/binary"
    14  	"fmt"
    15  
    16  	"github.com/usbarmory/tamago/internal/reg"
    17  )
    18  
    19  // Format of Setup Data (p276, Table 9-2, USB2.0)
    20  const (
    21  	REQUEST_TYPE_DIR = 7
    22  )
    23  
    24  // Standard request codes (p279, Table 9-4, USB2.0)
    25  const (
    26  	GET_STATUS        = 0
    27  	CLEAR_FEATURE     = 1
    28  	SET_FEATURE       = 3
    29  	SET_ADDRESS       = 5
    30  	GET_DESCRIPTOR    = 6
    31  	SET_DESCRIPTOR    = 7
    32  	GET_CONFIGURATION = 8
    33  	SET_CONFIGURATION = 9
    34  	GET_INTERFACE     = 10
    35  	SET_INTERFACE     = 11
    36  	SYNCH_FRAME       = 12
    37  )
    38  
    39  // Descriptor types (p279, Table 9-5, USB2.0)
    40  const (
    41  	DEVICE                    = 1
    42  	CONFIGURATION             = 2
    43  	STRING                    = 3
    44  	INTERFACE                 = 4
    45  	ENDPOINT                  = 5
    46  	DEVICE_QUALIFIER          = 6
    47  	OTHER_SPEED_CONFIGURATION = 7
    48  	INTERFACE_POWER           = 8
    49  
    50  	// Engineering Change Notices (ECN)
    51  	OTG                   = 9
    52  	DEBUG                 = 10
    53  	INTERFACE_ASSOCIATION = 11
    54  )
    55  
    56  // Standard feature selectors (p280, Table 9-6, USB2.0)
    57  const (
    58  	ENDPOINT_HALT        = 0
    59  	DEVICE_REMOTE_WAKEUP = 1
    60  	TEST_MODE            = 2
    61  )
    62  
    63  // SetupData implements
    64  // p276, Table 9-2. Format of Setup Data, USB2.0.
    65  type SetupData struct {
    66  	RequestType uint8
    67  	Request     uint8
    68  	Value       uint16
    69  	Index       uint16
    70  	Length      uint16
    71  }
    72  
    73  // swap adjusts the endianness of values written in memory by the hardware, as
    74  // they do not match the expected one by Go.
    75  func (s *SetupData) swap() {
    76  	b := make([]byte, 2)
    77  
    78  	binary.BigEndian.PutUint16(b, s.Value)
    79  	s.Value = binary.LittleEndian.Uint16(b)
    80  }
    81  
    82  func (hw *USB) getSetup() (setup *SetupData) {
    83  	setup = &SetupData{}
    84  
    85  	// p3801, 56.4.6.4.2.1 Setup Phase, IMX6ULLRM
    86  
    87  	*setup = hw.qh(0, OUT).Setup
    88  	setup.swap()
    89  
    90  	// clear setup status
    91  	reg.WriteBack(hw.setup)
    92  	// flush EP0 IN
    93  	reg.Set(hw.flush, ENDPTFLUSH_FETB+0)
    94  	// flush EP0 OUT
    95  	reg.Set(hw.flush, ENDPTFLUSH_FERB+0)
    96  
    97  	return
    98  }
    99  
   100  func (hw *USB) getDescriptor(setup *SetupData) (err error) {
   101  	bDescriptorType := setup.Value & 0xff
   102  	index := setup.Value >> 8
   103  
   104  	switch bDescriptorType {
   105  	case DEVICE:
   106  		err = hw.tx(0, trim(hw.Device.Descriptor.Bytes(), setup.Length))
   107  	case CONFIGURATION, OTHER_SPEED_CONFIGURATION:
   108  		if conf, err := hw.Device.Configuration(index); err == nil {
   109  			if bDescriptorType == OTHER_SPEED_CONFIGURATION {
   110  				conf[1] = byte(bDescriptorType)
   111  			}
   112  
   113  			err = hw.tx(0, trim(conf, setup.Length))
   114  		}
   115  	case STRING:
   116  		if int(index+1) > len(hw.Device.Strings) {
   117  			hw.stall(0, IN)
   118  			err = fmt.Errorf("invalid string descriptor index %d", index)
   119  		} else {
   120  			err = hw.tx(0, trim(hw.Device.Strings[index], setup.Length))
   121  		}
   122  	case DEVICE_QUALIFIER:
   123  		err = hw.tx(0, hw.Device.Qualifier.Bytes())
   124  	default:
   125  		hw.stall(0, IN)
   126  		err = fmt.Errorf("unsupported descriptor type: %#x", bDescriptorType)
   127  	}
   128  
   129  	return
   130  }
   131  
   132  func (hw *USB) handleSetup() (conf uint8, err error) {
   133  	setup := hw.getSetup()
   134  
   135  	if setup == nil {
   136  		return
   137  	}
   138  
   139  	if hw.Device.Setup != nil {
   140  		in, ack, done, err := hw.Device.Setup(setup)
   141  
   142  		if err != nil {
   143  			hw.stall(0, IN)
   144  			return 0, err
   145  		} else if len(in) != 0 {
   146  			err = hw.tx(0, in)
   147  		} else if ack {
   148  			err = hw.ack(0)
   149  		}
   150  
   151  		if done || err != nil {
   152  			return 0, err
   153  		}
   154  	}
   155  
   156  	switch setup.Request {
   157  	case GET_STATUS:
   158  		// no meaningful status to report for now
   159  		err = hw.tx(0, []byte{0x00, 0x00})
   160  	case CLEAR_FEATURE:
   161  		switch setup.Value {
   162  		case ENDPOINT_HALT:
   163  			n := int(setup.Index & 0xf)
   164  			dir := int(setup.Index&0x80) / 0x80
   165  
   166  			hw.reset(n, dir)
   167  			err = hw.ack(0)
   168  		default:
   169  			hw.stall(0, IN)
   170  		}
   171  	case SET_ADDRESS:
   172  		addr := uint32((setup.Value<<8)&0xff00 | (setup.Value >> 8))
   173  
   174  		reg.Set(hw.addr, DEVICEADDR_USBADRA)
   175  		reg.SetN(hw.addr, DEVICEADDR_USBADR, 0x7f, addr)
   176  
   177  		err = hw.ack(0)
   178  	case GET_DESCRIPTOR:
   179  		err = hw.getDescriptor(setup)
   180  	case GET_CONFIGURATION:
   181  		err = hw.tx(0, []byte{hw.Device.ConfigurationValue})
   182  	case SET_CONFIGURATION:
   183  		conf = uint8(setup.Value >> 8)
   184  
   185  		if hw.Device.ConfigurationValue != conf {
   186  			hw.Device.ConfigurationValue = conf
   187  		} else {
   188  			conf = 0
   189  		}
   190  
   191  		err = hw.ack(0)
   192  	case GET_INTERFACE:
   193  		err = hw.tx(0, []byte{hw.Device.AlternateSetting})
   194  	case SET_INTERFACE:
   195  		hw.Device.AlternateSetting = uint8(setup.Value >> 8)
   196  		err = hw.ack(0)
   197  	case SET_ETHERNET_PACKET_FILTER:
   198  		// no meaningful action for now
   199  		err = hw.ack(0)
   200  	default:
   201  		if (setup.RequestType >> REQUEST_TYPE_DIR) & 1 == OUT {
   202  			hw.stall(0, OUT)
   203  		}
   204  
   205  		hw.stall(0, IN)
   206  
   207  		err = fmt.Errorf("unsupported request code: %#x", setup.Request)
   208  	}
   209  
   210  	return
   211  }
   212  
   213  func trim(buf []byte, wLength uint16) []byte {
   214  	if int(wLength) < len(buf) {
   215  		buf = buf[0:wLength]
   216  	}
   217  
   218  	return buf
   219  }