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 }