Changeset 708d8fcd in mainline
- Timestamp:
- 2018-01-10T00:46:29Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- f92f6b1
- Parents:
- 4793023
- git-author:
- Ondřej Hlavatý <aearsis@…> (2018-01-09 19:26:55)
- git-committer:
- Ondřej Hlavatý <aearsis@…> (2018-01-10 00:46:29)
- Location:
- uspace
- Files:
-
- 2 added
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/bus/usb/xhci/Makefile
r4793023 r708d8fcd 39 39 endpoint.c \ 40 40 hc.c \ 41 isoch.c \ 41 42 main.c \ 42 43 rh.c \ -
uspace/drv/bus/usb/xhci/bus.c
r4793023 r708d8fcd 58 58 static endpoint_t *endpoint_create(device_t *, const usb_endpoint_descriptors_t *); 59 59 60 /** Ops receive generic bus_t pointer. */61 static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base)62 {63 assert(bus_base);64 return (xhci_bus_t *) bus_base;65 }66 67 60 /** 68 61 * Assign address and control endpoint to a new XHCI device. Once this function -
uspace/drv/bus/usb/xhci/bus.h
r4793023 r708d8fcd 59 59 int xhci_bus_remove_device(xhci_bus_t *, device_t *); 60 60 61 static inline xhci_bus_t *bus_to_xhci_bus(bus_t *bus_base) 62 { 63 assert(bus_base); 64 return (xhci_bus_t *) bus_base; 65 } 66 61 67 #endif 62 68 /** -
uspace/drv/bus/usb/xhci/endpoint.c
r4793023 r708d8fcd 84 84 */ 85 85 if (dev->speed >= USB_SPEED_HIGH || ep->transfer_type != USB_TRANSFER_INTERRUPT) { 86 xhci_ep->interval = desc->endpoint.poll_interval;86 xhci_ep->interval = 1 << (xhci_ep->interval - 1); 87 87 } 88 88 … … 92 92 } 93 93 94 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) { 95 xhci_ep->isoch->max_size = desc->companion.bytes_per_interval 96 ? desc->companion.bytes_per_interval 97 : ep->max_transfer_size; 98 /* Technically there could be superspeed plus too. */ 99 100 /* Allocate and setup isochronous-specific structures. */ 101 xhci_ep->isoch->enqueue = 0; 102 xhci_ep->isoch->dequeue = 0; 103 xhci_ep->isoch->started = false; 104 105 fibril_mutex_initialize(&xhci_ep->isoch->guard); 106 fibril_condvar_initialize(&xhci_ep->isoch->avail); 107 } 94 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) 95 isoch_init(xhci_ep, desc); 108 96 109 97 if ((rc = alloc_transfer_ds(xhci_ep))) … … 272 260 } 273 261 274 /** TODO document this275 */276 static int xhci_isoch_alloc_transfers(xhci_endpoint_t *xhci_ep) {277 int i = 0;278 int err = EOK;279 while (i < XHCI_ISOCH_BUFFER_COUNT) {280 xhci_isoch_transfer_t *transfer = &xhci_ep->isoch->transfers[i];281 if (dma_buffer_alloc(&transfer->data, xhci_ep->isoch->max_size)) {282 err = ENOMEM;283 break;284 }285 transfer->size = 0;286 ++i;287 }288 289 if (err) {290 --i;291 while(i >= 0) {292 dma_buffer_free(&xhci_ep->isoch->transfers[i].data);293 --i;294 }295 }296 297 return err;298 }299 300 262 /** Allocate transfer data structures for XHCI endpoint. 301 263 * @param[in] xhci_ep XHCI endpoint to allocate data structures for. … … 316 278 317 279 if (xhci_ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) { 318 if ((err = xhci_isoch_alloc_transfers(xhci_ep))) {280 if ((err = isoch_alloc_transfers(xhci_ep))) { 319 281 xhci_trb_ring_fini(&xhci_ep->ring); 320 282 return err; … … 427 389 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue); 428 390 XHCI_EP_DCS_SET(*ctx, 1); 429 XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32 - 1);391 XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32); 430 392 431 393 XHCI_EP_MAX_ESIT_PAYLOAD_LO_SET(*ctx, ep->isoch->max_size & 0xFFFF); … … 446 408 XHCI_EP_TR_DPTR_SET(*ctx, ep->ring.dequeue); 447 409 XHCI_EP_DCS_SET(*ctx, 1); 448 XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32 - 1);410 XHCI_EP_INTERVAL_SET(*ctx, fnzb32(ep->interval) % 32); 449 411 // TODO: max ESIT payload 450 412 } -
uspace/drv/bus/usb/xhci/endpoint.h
r4793023 r708d8fcd 45 45 #include <ddf/driver.h> 46 46 47 #include "isoch.h" 48 #include "transfers.h" 47 49 #include "trb_ring.h" 48 49 #include "transfers.h"50 50 51 51 typedef struct xhci_device xhci_device_t; … … 94 94 95 95 /** This field is a valid pointer for (and only for) isochronous transfers. */ 96 struct { 97 /** The maximum size of an isochronous transfer and therefore the size of buffers */ 98 size_t max_size; 99 100 /** Isochronous scheduled transfers with respective buffers */ 101 #define XHCI_ISOCH_BUFFER_COUNT 4 102 xhci_isoch_transfer_t transfers[XHCI_ISOCH_BUFFER_COUNT]; 103 104 /** Indices to transfers */ 105 size_t dequeue, enqueue; 106 107 /** Are isochronous transfers started? */ 108 bool started; 109 110 /** Protects common buffers. */ 111 fibril_mutex_t guard; 112 113 /** Signals filled buffer. */ 114 fibril_condvar_t avail; 115 } isoch [0]; 96 xhci_isoch_t isoch [0]; 116 97 } xhci_endpoint_t; 117 98 -
uspace/drv/bus/usb/xhci/hc.c
r4793023 r708d8fcd 192 192 hc->ac64 = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_AC64); 193 193 hc->max_slots = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_MAX_SLOTS); 194 unsigned ist = XHCI_REG_RD(hc->cap_regs, XHCI_CAP_IST); 195 hc->ist = (ist & 0x10 >> 1) * (ist & 0xf); 194 196 195 197 if ((err = hc_parse_ec(hc))) { … … 611 613 * Ring a xHC Doorbell. Implements section 4.7. 612 614 */ 613 inthc_ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target)615 void hc_ring_doorbell(xhci_hc_t *hc, unsigned doorbell, unsigned target) 614 616 { 615 617 assert(hc); … … 617 619 pio_write_32(&hc->db_arry[doorbell], v); 618 620 usb_log_debug2("Ringing doorbell %d (target: %d)", doorbell, target); 619 return EOK;620 621 } 621 622 -
uspace/drv/bus/usb/xhci/hc.h
r4793023 r708d8fcd 84 84 unsigned max_slots; 85 85 bool ac64; 86 unsigned ist; /**< IST in microframes */ 86 87 87 88 /** Port speed mapping */ … … 105 106 int hc_start(xhci_hc_t *, bool); 106 107 void hc_fini(xhci_hc_t *); 107 inthc_ring_doorbell(xhci_hc_t *, unsigned, unsigned);108 void hc_ring_doorbell(xhci_hc_t *, unsigned, unsigned); 108 109 int hc_enable_slot(xhci_hc_t *, uint32_t *); 109 110 int hc_disable_slot(xhci_hc_t *, xhci_device_t *); -
uspace/drv/bus/usb/xhci/hw_struct/regs.h
r4793023 r708d8fcd 481 481 } xhci_rt_regs_t; 482 482 483 #define XHCI_RT_MFINDEX mfindex, 32, FIELD 483 #define XHCI_RT_MFINDEX mfindex, 32, RANGE, 13, 0 484 #define XHCI_MFINDEX_MAX (1 << 14) 484 485 485 486 /** -
uspace/drv/bus/usb/xhci/hw_struct/trb.h
r4793023 r708d8fcd 147 147 xhci_dword_set_bits(&(trb).control, val, 17, 16) 148 148 149 #define TRB_ CTRL_SET_TBC(trb, val) \149 #define TRB_ISOCH_SET_TBC(trb, val) \ 150 150 xhci_dword_set_bits(&(trb).control, val, 8, 7) 151 #define TRB_ CTRL_SET_TLBPC(trb, val) \151 #define TRB_ISOCH_SET_TLBPC(trb, val) \ 152 152 xhci_dword_set_bits(&(trb).control, val, 19, 16) 153 #define TRB_CTRL_SET_SIA(trb, val) \ 153 #define TRB_ISOCH_SET_FRAMEID(trb, val) \ 154 xhci_dword_set_bits(&(trb).control, val, 30, 20) 155 #define TRB_ISOCH_SET_SIA(trb, val) \ 154 156 xhci_dword_set_bits(&(trb).control, val, 31, 31) 155 157 -
uspace/drv/bus/usb/xhci/transfers.c
r4793023 r708d8fcd 236 236 } 237 237 238 static xhci_isoch_transfer_t* isoch_transfer_get_enqueue(xhci_endpoint_t *ep) { 239 if (((ep->isoch->enqueue + 1) % XHCI_ISOCH_BUFFER_COUNT) == ep->isoch->dequeue) { 240 /* None ready */ 241 return NULL; 242 } 243 xhci_isoch_transfer_t *isoch_transfer = &ep->isoch->transfers[ep->isoch->enqueue]; 244 ep->isoch->enqueue = (ep->isoch->enqueue + 1) % XHCI_ISOCH_BUFFER_COUNT; 245 return isoch_transfer; 246 } 247 248 static xhci_isoch_transfer_t* isoch_transfer_get_dequeue(xhci_endpoint_t *ep) { 249 xhci_isoch_transfer_t *isoch_transfer = &ep->isoch->transfers[ep->isoch->dequeue]; 250 ep->isoch->dequeue = (ep->isoch->dequeue + 1) % XHCI_ISOCH_BUFFER_COUNT; 251 return isoch_transfer; 252 } 253 254 static int schedule_isochronous_trb(xhci_trb_ring_t *ring, xhci_endpoint_t *ep, xhci_trb_t *trb, 255 const size_t len, uintptr_t *trb_phys) 256 { 257 TRB_CTRL_SET_XFER_LEN(*trb, len); 258 // FIXME: TD size 4.11.2.4 (there is no next TRB, so 0?) 259 TRB_CTRL_SET_TD_SIZE(*trb, 0); 260 TRB_CTRL_SET_IOC(*trb, 1); 261 TRB_CTRL_SET_TRB_TYPE(*trb, XHCI_TRB_TYPE_ISOCH); 262 263 // see 4.14.1 and 4.11.2.3 for the explanation, how to calculate those 264 size_t tdpc = len / 1024 + ((len % 1024) ? 1 : 0); 265 size_t tbc = tdpc / ep->max_burst; 266 if (!tdpc % ep->max_burst) --tbc; 267 size_t bsp = tdpc % ep->max_burst; 268 size_t tlbpc = (bsp ? bsp : ep->max_burst) - 1; 269 270 TRB_CTRL_SET_TBC(*trb, tbc); 271 TRB_CTRL_SET_TLBPC(*trb, tlbpc); 272 273 // FIXME: do we want this? 6.4.1.3, p 366 (also possibly frame id?) 274 TRB_CTRL_SET_SIA(*trb, 1); 275 276 return xhci_trb_ring_enqueue(ring, trb, trb_phys); 277 } 278 279 static int schedule_isochronous_out(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep, 280 xhci_device_t *xhci_dev) 281 { 282 xhci_trb_t trb; 283 xhci_trb_clean(&trb); 284 285 fibril_mutex_lock(&xhci_ep->isoch->guard); 286 xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_enqueue(xhci_ep); 287 while (!isoch_transfer) { 288 fibril_condvar_wait(&xhci_ep->isoch->avail, &xhci_ep->isoch->guard); 289 isoch_transfer = isoch_transfer_get_enqueue(xhci_ep); 290 } 291 292 isoch_transfer->size = transfer->batch.buffer_size; 293 if (isoch_transfer->size > 0) { 294 memcpy(isoch_transfer->data.virt, transfer->batch.buffer, isoch_transfer->size); 295 } 296 297 trb.parameter = isoch_transfer->data.phys; 298 299 xhci_trb_ring_t *ring = get_ring(hc, transfer); 300 int err = schedule_isochronous_trb(ring, xhci_ep, &trb, isoch_transfer->size, 301 &isoch_transfer->interrupt_trb_phys); 302 if (err) { 303 fibril_mutex_unlock(&xhci_ep->isoch->guard); 304 return err; 305 } 306 307 /* If not yet started, start the isochronous endpoint transfers - after buffer count - 1 writes */ 308 /* The -1 is there because of the enqueue != dequeue check. The buffer must have at least 2 transfers. */ 309 if (((xhci_ep->isoch->enqueue + 1) % XHCI_ISOCH_BUFFER_COUNT) == xhci_ep->isoch->dequeue && !xhci_ep->isoch->started) { 310 const uint8_t slot_id = xhci_dev->slot_id; 311 const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */ 312 err = hc_ring_doorbell(hc, slot_id, target); 313 xhci_ep->isoch->started = true; 314 } 315 fibril_mutex_unlock(&xhci_ep->isoch->guard); 316 if (err) { 317 return err; 318 } 319 320 /* Isochronous transfers don't handle errors, they skip them all. */ 321 transfer->batch.error = EOK; 322 transfer->batch.transfered_size = transfer->batch.buffer_size; 323 usb_transfer_batch_finish(&transfer->batch); 324 return EOK; 325 } 326 327 static int schedule_isochronous_in_trbs(xhci_endpoint_t *xhci_ep, xhci_trb_ring_t *ring) { 328 xhci_trb_t trb; 329 xhci_isoch_transfer_t *isoch_transfer; 330 while ((isoch_transfer = isoch_transfer_get_enqueue(xhci_ep)) != NULL) { 331 xhci_trb_clean(&trb); 332 trb.parameter = isoch_transfer->data.phys; 333 isoch_transfer->size = xhci_ep->isoch->max_size; 334 335 int err = schedule_isochronous_trb(ring, xhci_ep, &trb, isoch_transfer->size, 336 &isoch_transfer->interrupt_trb_phys); 337 if (err) 338 return err; 339 } 340 return EOK; 341 } 342 343 static int schedule_isochronous_in(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep, 344 xhci_device_t *xhci_dev) 345 { 346 fibril_mutex_lock(&xhci_ep->isoch->guard); 347 /* If not yet started, start the isochronous endpoint transfers - before first read */ 348 if (!xhci_ep->isoch->started) { 349 xhci_trb_ring_t *ring = get_ring(hc, transfer); 350 /* Fill the TRB ring. */ 351 int err = schedule_isochronous_in_trbs(xhci_ep, ring); 352 if (err) { 353 fibril_mutex_unlock(&xhci_ep->isoch->guard); 354 return err; 355 } 356 /* Ring the doorbell to start it. */ 357 const uint8_t slot_id = xhci_dev->slot_id; 358 const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */ 359 err = hc_ring_doorbell(hc, slot_id, target); 360 if (err) { 361 fibril_mutex_unlock(&xhci_ep->isoch->guard); 362 return err; 363 } 364 xhci_ep->isoch->started = true; 365 } 366 367 xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_enqueue(xhci_ep); 368 while(!isoch_transfer) { 369 fibril_condvar_wait(&xhci_ep->isoch->avail, &xhci_ep->isoch->guard); 370 isoch_transfer = isoch_transfer_get_enqueue(xhci_ep); 371 } 372 373 isoch_transfer->size = transfer->batch.buffer_size; 374 if (transfer->batch.buffer_size <= isoch_transfer->size) { 375 if (transfer->batch.buffer_size > 0) { 376 memcpy(transfer->batch.buffer, isoch_transfer->data.virt, transfer->batch.buffer_size); 377 } 378 if (transfer->batch.buffer_size < isoch_transfer->size) { 379 // FIXME: somehow notify that buffer was too small, probably batch error code 380 } 381 transfer->batch.transfered_size = transfer->batch.buffer_size; 382 } 383 else { 384 memcpy(transfer->batch.buffer, isoch_transfer->data.virt, isoch_transfer->size); 385 transfer->batch.transfered_size = isoch_transfer->size; 386 } 387 388 // Clear and requeue the transfer with new TRB 389 xhci_trb_t trb; 390 xhci_trb_clean(&trb); 391 392 trb.parameter = isoch_transfer->data.phys; 393 isoch_transfer->size = xhci_ep->isoch->max_size; 394 395 xhci_trb_ring_t *ring = get_ring(hc, transfer); 396 int err = schedule_isochronous_trb(ring, xhci_ep, &trb, isoch_transfer->size, 397 &isoch_transfer->interrupt_trb_phys); 398 fibril_mutex_unlock(&xhci_ep->isoch->guard); 399 400 if (err) { 401 return err; 402 } 403 404 /* Isochronous transfers don't handle errors, they skip them all. */ 405 transfer->batch.error = EOK; 406 usb_transfer_batch_finish(&transfer->batch); 407 return EOK; 408 } 409 410 static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep, 411 xhci_device_t *xhci_dev) 412 { 413 if (transfer->batch.buffer_size > xhci_ep->isoch->max_size) { 414 usb_log_error("Cannot schedule an oversized isochronous transfer."); 415 return EINVAL; 416 } 417 418 if (xhci_ep->base.direction == USB_DIRECTION_OUT) { 419 return schedule_isochronous_out(hc, transfer, xhci_ep, xhci_dev); 420 } 421 else { 422 return schedule_isochronous_in(hc, transfer, xhci_ep, xhci_dev); 423 } 424 } 425 426 static int handle_isochronous_transfer_event(xhci_hc_t *hc, xhci_trb_t *trb, xhci_endpoint_t *ep) { 427 fibril_mutex_lock(&ep->isoch->guard); 428 429 int err = EOK; 430 431 const xhci_trb_completion_code_t completion_code = TRB_COMPLETION_CODE(*trb); 432 switch (completion_code) { 433 case XHCI_TRBC_RING_OVERRUN: 434 case XHCI_TRBC_RING_UNDERRUN: 435 /* Rings are unscheduled by xHC now */ 436 ep->isoch->started = false; 437 /* For OUT, there was nothing to process */ 438 /* For IN, the buffer has overfilled, we empty the buffers and readd TRBs */ 439 ep->isoch->enqueue = ep->isoch->dequeue = 0; 440 err = EIO; 441 break; 442 case XHCI_TRBC_SHORT_PACKET: 443 usb_log_debug("Short transfer."); 444 /* fallthrough */ 445 case XHCI_TRBC_SUCCESS: 446 break; 447 default: 448 usb_log_warning("Transfer not successfull: %u", completion_code); 449 err = EIO; 450 } 451 452 xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_dequeue(ep); 453 if (isoch_transfer->interrupt_trb_phys != trb->parameter) { 454 usb_log_error("Non-matching trb to isochronous transfer, skipping."); 455 // FIXME: what to do? probably just kill the whole endpoint 456 err = ENOENT; 457 } 458 459 if (ep->base.direction == USB_DIRECTION_IN) { 460 // We may have received less data, that's fine 461 isoch_transfer->size -= TRB_TRANSFER_LENGTH(*trb); 462 } 463 464 fibril_condvar_signal(&ep->isoch->avail); 465 fibril_mutex_unlock(&ep->isoch->guard); 466 return err; 238 static int schedule_isochronous(xhci_transfer_t* transfer) 239 { 240 endpoint_t *ep = transfer->batch.ep; 241 242 return ep->direction == USB_DIRECTION_OUT 243 ? isoch_schedule_out(transfer) 244 : isoch_schedule_in(transfer); 467 245 } 468 246 … … 486 264 return ENOENT; 487 265 } 266 // No need to add reference for endpoint, it is held by the transfer batch. 488 267 489 268 /* FIXME: This is racy. Do we care? */ … … 491 270 492 271 if (ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) { 493 return handle_isochronous_transfer_event(hc, trb, ep);272 return isoch_handle_transfer_event(hc, ep, trb); 494 273 } 495 274 … … 558 337 // Isochronous transfer needs to be handled differently 559 338 if (batch->ep->transfer_type == USB_TRANSFER_ISOCHRONOUS) { 560 return schedule_isochronous( hc, transfer, xhci_ep, xhci_dev);339 return schedule_isochronous(transfer); 561 340 } 562 341 … … 591 370 const uint8_t slot_id = xhci_dev->slot_id; 592 371 const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */ 593 return hc_ring_doorbell(hc, slot_id, target); 594 } 372 hc_ring_doorbell(hc, slot_id, target); 373 return EOK; 374 } -
uspace/drv/bus/usb/xhci/transfers.h
r4793023 r708d8fcd 55 55 } xhci_transfer_t; 56 56 57 typedef struct {58 /* Used buffer size */59 uint64_t size;60 /* Buffer with data */61 dma_buffer_t data;62 /* Physical address of enqueued TRB */63 uintptr_t interrupt_trb_phys;64 } xhci_isoch_transfer_t;65 66 57 usb_transfer_batch_t* xhci_transfer_create(endpoint_t *); 67 58 int xhci_transfer_schedule(xhci_hc_t *, usb_transfer_batch_t *); -
uspace/lib/drv/include/usbhc_iface.h
r4793023 r708d8fcd 64 64 * Callers shall fill it with bare contents of respective descriptors (in usb endianity). 65 65 */ 66 typedef struct {66 typedef struct usb_endpoint_descriptors { 67 67 struct { 68 68 uint8_t endpoint_address;
Note:
See TracChangeset
for help on using the changeset viewer.