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 }