github.com/f-secure-foundry/tamago@v0.0.0-20220307101044-d73fcdd7f11b/soc/imx6/usb/endpoint.go (about)

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