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

     1  // NXP USBOH3USBO2 / USBPHY driver
     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  	"bytes"
    14  	"encoding/binary"
    15  	"fmt"
    16  
    17  	"github.com/usbarmory/tamago/bits"
    18  	"github.com/usbarmory/tamago/dma"
    19  	"github.com/usbarmory/tamago/internal/reg"
    20  )
    21  
    22  // Endpoint constants
    23  const (
    24  	// The USB OTG device controller hardware supports up to 8 endpoint
    25  	// numbers.
    26  	MAX_ENDPOINTS = 8
    27  
    28  	// Host -> Device
    29  	OUT = 0
    30  	// Device -> Host
    31  	IN = 1
    32  
    33  	// Transfer Type
    34  	CONTROL     = 0
    35  	ISOCHRONOUS = 1
    36  	BULK        = 2
    37  	INTERRUPT   = 3
    38  
    39  	// p3784, 56.4.5.1 Endpoint Queue Head (dQH), IMX6ULLRM
    40  	DQH_LIST_ALIGN = 2048
    41  	DQH_ALIGN      = 64
    42  	DQH_SIZE       = 64
    43  
    44  	DQH_INFO  = 0
    45  	INFO_MULT = 30
    46  	INFO_ZLT  = 29
    47  	INFO_MPL  = 16
    48  	INFO_IOS  = 15
    49  
    50  	DQH_CURRENT = 4
    51  	DQH_NEXT    = 8
    52  	DQH_TOKEN   = 12
    53  
    54  	// p3787, 56.4.5.2 Endpoint Transfer Descriptor (dTD), IMX6ULLRM
    55  	DTD_ALIGN     = 32
    56  	DTD_SIZE      = 28
    57  	DTD_PAGES     = 5
    58  	DTD_PAGE_SIZE = 4096
    59  	DTD_NEXT      = 0
    60  
    61  	DTD_TOKEN    = 4
    62  	TOKEN_TOTAL  = 16
    63  	TOKEN_IOC    = 15
    64  	TOKEN_MULTO  = 10
    65  	TOKEN_ACTIVE = 7
    66  	TOKEN_STATUS = 0
    67  )
    68  
    69  // dTD implements
    70  // p3787, 56.4.5.2 Endpoint Transfer Descriptor (dTD), IMX6ULLRM.
    71  type dTD struct {
    72  	Next   uint32
    73  	Token  uint32
    74  	Buffer [5]uint32
    75  
    76  	// DMA pointer for dTD structure
    77  	_dtd uint32
    78  	// DMA pointer for dTD transfer buffer
    79  	_buf uint32
    80  	// transfer buffer size
    81  	_size uint32
    82  }
    83  
    84  // dQH implements
    85  // p3784, 56.4.5.1 Endpoint Queue Head (dQH), IMX6ULLRM.
    86  type dQH struct {
    87  	Info    uint32
    88  	Current uint32
    89  	Next    uint32
    90  	Token   uint32
    91  	Buffer  [5]uint32
    92  
    93  	// reserved
    94  	_ uint32
    95  
    96  	// The Set-up Buffer will be filled by hardware, note that after this
    97  	// happens endianess needs to be adjusted with SetupData.swap().
    98  	Setup SetupData
    99  
   100  	// We align only the first queue entry, so we need a 4*uint32 gap to
   101  	// maintain 64-byte boundaries.
   102  	_ [4]uint32
   103  }
   104  
   105  // endpointList implements
   106  // p3783, 56.4.5 Device Data Structures, IMX6ULLRM.
   107  type endpointList [MAX_ENDPOINTS * 2]dQH
   108  
   109  // initQH initializes the endpoint queue head list
   110  func (hw *USB) initQH() {
   111  	var epList endpointList
   112  	buf := new(bytes.Buffer)
   113  
   114  	binary.Write(buf, binary.LittleEndian, &epList)
   115  	hw.epListAddr = uint32(dma.Alloc(buf.Bytes(), DQH_LIST_ALIGN))
   116  
   117  	// set endpoint queue head
   118  	reg.Write(hw.eplist, hw.epListAddr)
   119  }
   120  
   121  // set configures an endpoint queue head as described in
   122  // p3784, 56.4.5.1 Endpoint Queue Head, IMX6ULLRM.
   123  func (hw *USB) set(n int, dir int, max int, zlt bool, mult int) {
   124  	dqh := dQH{}
   125  
   126  	// Mult
   127  	bits.SetN(&dqh.Info, INFO_MULT, 0b11, uint32(mult))
   128  	// Maximum Packet Length
   129  	bits.SetN(&dqh.Info, INFO_MPL, 0x7ff, uint32(max))
   130  
   131  	if !zlt {
   132  		// Zero Length Termination must be disabled for multi dTD
   133  		// requests.
   134  		bits.SetN(&dqh.Info, INFO_ZLT, 1, 1)
   135  	}
   136  
   137  	if n == 0 {
   138  		// interrupt on setup (ios)
   139  		bits.Set(&dqh.Info, INFO_IOS)
   140  	}
   141  
   142  	// Total bytes
   143  	bits.SetN(&dqh.Token, TOKEN_TOTAL, 0xffff, 0)
   144  	// interrupt on completion (ioc)
   145  	bits.Set(&dqh.Token, TOKEN_IOC)
   146  	// multiplier override (MultO)
   147  	bits.SetN(&dqh.Token, TOKEN_MULTO, 0b11, 0)
   148  
   149  	buf := new(bytes.Buffer)
   150  	binary.Write(buf, binary.LittleEndian, &dqh)
   151  
   152  	offset := (n*2 + dir) * DQH_SIZE
   153  	dma.Write(uint(hw.epListAddr), offset, buf.Bytes())
   154  
   155  	hw.dQH[n][dir] = hw.epListAddr + uint32(offset)
   156  }
   157  
   158  // enable enables an endpoint.
   159  func (hw *USB) enable(n int, dir int, transferType int) {
   160  	if n == 0 {
   161  		// EP0 does not need enabling (p3790, IMX6ULLRM)
   162  		return
   163  	}
   164  
   165  	ctrl := hw.epctrl + uint32(4*n)
   166  	c := reg.Read(ctrl)
   167  
   168  	if dir == IN {
   169  		bits.Set(&c, ENDPTCTRL_TXE)
   170  		bits.Set(&c, ENDPTCTRL_TXR)
   171  		bits.SetN(&c, ENDPTCTRL_TXT, 0b11, uint32(transferType))
   172  		bits.Clear(&c, ENDPTCTRL_TXS)
   173  
   174  		if reg.Get(ctrl, ENDPTCTRL_RXE, 1) == 0 {
   175  			// see note at p3879 of IMX6ULLRM
   176  			bits.SetN(&c, ENDPTCTRL_RXT, 0b11, BULK)
   177  		}
   178  	} else {
   179  		bits.Set(&c, ENDPTCTRL_RXE)
   180  		bits.Set(&c, ENDPTCTRL_RXR)
   181  		bits.SetN(&c, ENDPTCTRL_RXT, 0b11, uint32(transferType))
   182  		bits.Clear(&c, ENDPTCTRL_RXS)
   183  
   184  		if reg.Get(ctrl, ENDPTCTRL_TXE, 1) == 0 {
   185  			// see note at p3879 of IMX6ULLRM
   186  			bits.SetN(&c, ENDPTCTRL_TXT, 0b11, BULK)
   187  		}
   188  	}
   189  
   190  	reg.Write(ctrl, c)
   191  }
   192  
   193  // clear resets the endpoint status (active and halt bits)
   194  func (hw *USB) clear(n int, dir int) {
   195  	token := hw.dQH[n][dir] + DQH_TOKEN
   196  	reg.SetN(token, TOKEN_STATUS, 0xc0, 0)
   197  }
   198  
   199  // qh returns the Endpoint Queue Head (dQH)
   200  func (hw *USB) qh(n int, dir int) (dqh dQH) {
   201  	buf := make([]byte, DQH_SIZE)
   202  	dma.Read(uint(hw.dQH[n][dir]), 0, buf)
   203  
   204  	err := binary.Read(bytes.NewReader(buf), binary.LittleEndian, &dqh)
   205  
   206  	if err != nil {
   207  		panic(err)
   208  	}
   209  
   210  	return
   211  }
   212  
   213  // nextDTD sets the next endpoint transfer pointer
   214  func (hw *USB) nextDTD(n int, dir int, dtd uint32) {
   215  	dqh := hw.dQH[n][dir]
   216  	next := dqh + DQH_NEXT
   217  
   218  	// wait for endpoint status to be cleared
   219  	reg.Wait(dqh+DQH_TOKEN, TOKEN_STATUS, 0xc0, 0)
   220  	// set next dTD
   221  	reg.Write(next, dtd)
   222  }
   223  
   224  // buildDTD configures an endpoint transfer descriptor as described in
   225  // p3787, 56.4.5.2 Endpoint Transfer Descriptor (dTD), IMX6ULLRM.
   226  func buildDTD(n int, dir int, addr uint32, size int) (dtd *dTD) {
   227  	// p3809, 56.4.6.6.2 Building a Transfer Descriptor, IMX6ULLRM
   228  	dtd = &dTD{}
   229  
   230  	// interrupt on completion (ioc)
   231  	bits.Set(&dtd.Token, TOKEN_IOC)
   232  
   233  	// invalidate next pointer
   234  	dtd.Next = 1
   235  	// multiplier override (MultO)
   236  	bits.SetN(&dtd.Token, TOKEN_MULTO, 0b11, 0)
   237  	// active status
   238  	bits.Set(&dtd.Token, TOKEN_ACTIVE)
   239  	// total bytes
   240  	bits.SetN(&dtd.Token, TOKEN_TOTAL, 0xffff, uint32(size))
   241  
   242  	dtd._buf = addr
   243  	dtd._size = uint32(size)
   244  
   245  	for n := 0; n < DTD_PAGES; n++ {
   246  		dtd.Buffer[n] = dtd._buf + DTD_PAGE_SIZE*uint32(n)
   247  	}
   248  
   249  	buf := new(bytes.Buffer)
   250  	binary.Write(buf, binary.LittleEndian, dtd)
   251  
   252  	// skip internal DMA buffer pointers
   253  	dtd._dtd = uint32(dma.Alloc(buf.Bytes()[0:DTD_SIZE], DTD_ALIGN))
   254  
   255  	return
   256  }
   257  
   258  // checkDTD verifies transfer descriptor completion as describe in
   259  // p3800, 56.4.6.4.1 Interrupt/Bulk Endpoint Operational Model, IMX6ULLRM
   260  // p3811, 56.4.6.6.4 Transfer Completion, IMX6ULLRM.
   261  func (hw *USB) checkDTD(n int, dir int, dtds []*dTD) (size int, err error) {
   262  	for i, dtd := range dtds {
   263  		// treat dtd.token as a register within the dtd DMA buffer
   264  		token := dtd._dtd + DTD_TOKEN
   265  
   266  		// wait for active bit to be cleared
   267  		reg.WaitSignal(hw.exit, token, TOKEN_ACTIVE, 1, 0)
   268  
   269  		dtdToken := reg.Read(token)
   270  
   271  		if (dtdToken & 0xff) != 0 {
   272  			return 0, fmt.Errorf("dTD[%d] error status, token:%#x", i, dtdToken)
   273  		}
   274  
   275  		// p3787 "This field is decremented by the number of bytes
   276  		// actually moved during the transaction", IMX6ULLRM.
   277  		rest := dtdToken >> TOKEN_TOTAL
   278  		n := int(dtd._size - rest)
   279  
   280  		if dir == IN && rest > 0 {
   281  			return 0, fmt.Errorf("dTD[%d] partial transfer (%d/%d bytes)", i, n, dtd._size)
   282  		}
   283  
   284  		size += n
   285  	}
   286  
   287  	return
   288  }
   289  
   290  // transfer initates a transfer using transfer descriptors (dTDs) as described in
   291  // p3810, 56.4.6.6.3 Executing A Transfer Descriptor, IMX6ULLRM.
   292  func (hw *USB) transfer(n int, dir int, buf []byte) (out []byte, err error) {
   293  	var dtds []*dTD
   294  	var prev *dTD
   295  	var i int
   296  
   297  	// hw.prime IN:ENDPTPRIME_PETB+n    OUT:ENDPTPRIME_PERB+n
   298  	// hw.pos   IN:ENDPTCOMPLETE_ETCE+n OUT:ENDPTCOMPLETE_ERCE+n
   299  	pos := (dir * 16) + n
   300  
   301  	dtdLength := DTD_PAGES * DTD_PAGE_SIZE
   302  
   303  	if dir == OUT && buf == nil {
   304  		buf = make([]byte, dtdLength)
   305  	}
   306  
   307  	transferSize := len(buf)
   308  
   309  	pages := dma.Alloc(buf, DTD_PAGE_SIZE)
   310  	defer dma.Free(pages)
   311  
   312  	// loop condition to account for zero transferSize
   313  	for add := true; add; add = i < transferSize {
   314  		prime := false
   315  		size := dtdLength
   316  
   317  		if i+size > transferSize {
   318  			size = transferSize - i
   319  		}
   320  
   321  		dtd := buildDTD(n, dir, uint32(pages)+uint32(i), size)
   322  		defer dma.Free(uint(dtd._dtd))
   323  
   324  		if i == 0 {
   325  			prime = true
   326  		} else {
   327  			// treat dtd.next as a register within the dtd DMA buffer
   328  			reg.Write(prev._dtd+DTD_NEXT, dtd._dtd)
   329  			prime = reg.Get(hw.prime, pos, 1) == 0 && reg.Get(hw.stat, pos, 1) == 0
   330  		}
   331  
   332  		if prime {
   333  			// reset endpoint status
   334  			hw.clear(n, dir)
   335  			// set dQH head pointer
   336  			hw.nextDTD(n, dir, dtd._dtd)
   337  			// prime endpoint
   338  			reg.Set(hw.prime, pos)
   339  		}
   340  
   341  		prev = dtd
   342  		dtds = append(dtds, dtd)
   343  
   344  		i += dtdLength
   345  	}
   346  
   347  	// wait for priming completion
   348  	reg.Wait(hw.prime, pos, 1, 0)
   349  
   350  	if hw.event != nil && n != 0 {
   351  		// wait for completion (event)
   352  		for reg.Get(hw.complete, pos, 1) != 1 {
   353  			hw.event.L.Lock()
   354  			hw.event.Wait()
   355  			hw.event.L.Unlock()
   356  		}
   357  	} else {
   358  		// wait for completion (poll)
   359  		reg.WaitSignal(hw.exit, hw.complete, pos, 1, 1)
   360  	}
   361  
   362  	// clear completion
   363  	reg.Write(hw.complete, 1<<pos)
   364  
   365  	size, err := hw.checkDTD(n, dir, dtds)
   366  
   367  	if n != 0 && dir == OUT && buf != nil {
   368  		out = buf[0:size]
   369  		dma.Read(pages, 0, out)
   370  	}
   371  
   372  	return
   373  }
   374  
   375  // ack transmits a zero length packet to the host through an IN endpoint
   376  func (hw *USB) ack(n int) (err error) {
   377  	_, err = hw.transfer(n, IN, nil)
   378  	return
   379  }
   380  
   381  // tx transmits a data buffer to the host through an IN endpoint
   382  func (hw *USB) tx(n int, in []byte) (err error) {
   383  	_, err = hw.transfer(n, IN, in)
   384  
   385  	// p3803, 56.4.6.4.2.3 Status Phase, IMX6ULLRM
   386  	if err == nil && n == 0 {
   387  		_, err = hw.transfer(n, OUT, nil)
   388  	}
   389  
   390  	return
   391  }
   392  
   393  // rx receives a data buffer from the host through an OUT endpoint
   394  func (hw *USB) rx(n int, buf []byte) (out []byte, err error) {
   395  	return hw.transfer(n, OUT, buf)
   396  }
   397  
   398  // stall forces the endpoint to return a STALL handshake to the host
   399  func (hw *USB) stall(n int, dir int) {
   400  	ctrl := hw.epctrl + uint32(4*n)
   401  
   402  	if dir == IN {
   403  		reg.Set(ctrl, ENDPTCTRL_TXS)
   404  	} else {
   405  		reg.Set(ctrl, ENDPTCTRL_RXS)
   406  	}
   407  }
   408  
   409  // reset forces data PID synchronization between host and device
   410  func (hw *USB) reset(n int, dir int) {
   411  	if n == 0 {
   412  		// EP0 does not have data toggle reset
   413  		return
   414  	}
   415  
   416  	ctrl := hw.epctrl + uint32(4*n)
   417  
   418  	if dir == IN {
   419  		reg.Set(ctrl, ENDPTCTRL_TXR)
   420  	} else {
   421  		reg.Set(ctrl, ENDPTCTRL_RXR)
   422  	}
   423  }