Changeset d3086873 in mainline
- Timestamp:
- 2017-11-20T17:23:12Z (7 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- 6b433a8
- Parents:
- 888238e9
- git-author:
- Salmelu <salmelu@…> (2017-11-20 17:22:34)
- git-committer:
- Salmelu <salmelu@…> (2017-11-20 17:23:12)
- Location:
- uspace/drv/bus/usb/xhci
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/drv/bus/usb/xhci/endpoint.h
r888238e9 rd3086873 89 89 /** Maximum number of bursts within an interval that this endpoint supports */ 90 90 uint8_t mult; 91 92 /** The maximum size of an isochronous transfer and therefore the size of buffers */ 93 size_t isoch_max_size; 94 95 /** Isochronous scheduled transfers with respective buffers */ 96 #define XHCI_ISOCH_BUFFER_COUNT 4 97 xhci_isoch_transfer_t* isoch_transfers[XHCI_ISOCH_BUFFER_COUNT]; 98 99 /** Indices to transfers */ 100 size_t isoch_dequeue, isoch_enqueue; 101 102 /** Are isochronous transfers started? */ 103 bool isoch_started; 91 104 } xhci_endpoint_t; 92 105 -
uspace/drv/bus/usb/xhci/hw_struct/trb.h
r888238e9 rd3086873 146 146 #define TRB_CTRL_SET_TRT(trb, val) \ 147 147 xhci_dword_set_bits(&(trb).control, val, 17, 16) 148 149 #define TRB_CTRL_SET_TBC(trb, val) \ 150 xhci_dword_set_bits(&(trb).control, val, 8, 7) 151 #define TRB_CTRL_SET_TLBPC(trb, val) \ 152 xhci_dword_set_bits(&(trb).control, val, 19, 16) 153 #define TRB_CTRL_SET_SIA(trb, val) \ 154 xhci_dword_set_bits(&(trb).control, val, 31, 31) 148 155 149 156 /** -
uspace/drv/bus/usb/xhci/transfers.c
r888238e9 rd3086873 232 232 } 233 233 234 static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer) 235 { 236 /* TODO: Implement me. */ 237 usb_log_error("Isochronous transfers are not yet implemented!"); 238 return ENOTSUP; 234 static xhci_isoch_transfer_t* isoch_transfer_get_enqueue(xhci_endpoint_t *ep) { 235 if ((ep->isoch_enqueue % XHCI_ISOCH_BUFFER_COUNT) == ep->isoch_dequeue) { 236 /* None ready */ 237 return NULL; 238 } 239 xhci_isoch_transfer_t *isoch_transfer = ep->isoch_transfers[ep->isoch_enqueue]; 240 ep->isoch_enqueue = (ep->isoch_enqueue + 1) % XHCI_ISOCH_BUFFER_COUNT; 241 return isoch_transfer; 242 } 243 244 static xhci_isoch_transfer_t* isoch_transfer_get_dequeue(xhci_endpoint_t *ep) { 245 xhci_isoch_transfer_t *isoch_transfer = ep->isoch_transfers[ep->isoch_dequeue]; 246 ep->isoch_dequeue = (ep->isoch_dequeue + 1) % XHCI_ISOCH_BUFFER_COUNT; 247 return isoch_transfer; 248 } 249 250 static int schedule_isochronous_trb(xhci_trb_ring_t *ring, xhci_endpoint_t *ep, xhci_trb_t *trb, 251 const size_t len, uintptr_t *trb_phys) 252 { 253 TRB_CTRL_SET_XFER_LEN(*trb, len); 254 // FIXME: TD size 4.11.2.4 (there is no next TRB, so 0?) 255 TRB_CTRL_SET_TD_SIZE(*trb, 0); 256 TRB_CTRL_SET_IOC(*trb, 1); 257 TRB_CTRL_SET_TRB_TYPE(*trb, XHCI_TRB_TYPE_ISOCH); 258 259 // see 4.14.1 and 4.11.2.3 for the explanation, how to calculate those 260 size_t tdpc = len / 1024 + ((len % 1024) ? 1 : 0); 261 size_t tbc = tdpc / (ep->max_burst + 1); 262 if(!tdpc % (ep->max_burst + 1)) --tbc; 263 size_t bsp = tdpc % (ep->max_burst + 1); 264 size_t tlbpc = (bsp ? bsp - 1 : ep->max_burst); 265 266 TRB_CTRL_SET_TBC(*trb, tbc); 267 TRB_CTRL_SET_TLBPC(*trb, tlbpc); 268 269 // FIXME: do we want this? 6.4.1.3, p 366 (also possibly frame id?) 270 TRB_CTRL_SET_SIA(*trb, 1); 271 272 return xhci_trb_ring_enqueue(ring, trb, trb_phys); 273 } 274 275 static int schedule_isochronous_out(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep, 276 xhci_device_t *xhci_dev) 277 { 278 xhci_trb_t trb; 279 xhci_trb_clean(&trb); 280 281 // FIXME add some locks 282 xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_enqueue(xhci_ep); 283 while (!isoch_transfer) { 284 // FIXME: add condvar sleep; 285 } 286 287 isoch_transfer->size = transfer->batch.buffer_size; 288 if (isoch_transfer->size > 0) { 289 memcpy(isoch_transfer->data_virt, transfer->batch.buffer, isoch_transfer->size); 290 } 291 292 trb.parameter = isoch_transfer->data_phys; 293 294 xhci_trb_ring_t *ring = get_ring(hc, transfer); 295 int err = schedule_isochronous_trb(ring, xhci_ep, &trb, isoch_transfer->size, 296 &isoch_transfer->interrupt_trb_phys); 297 if (err) { 298 return err; 299 } 300 301 /* If not yet started, start the isochronous endpoint transfers - after buffer count - 1 writes */ 302 /* The -2 is there because of the enqueue != dequeue check. The buffer must have at least 2 transfers. */ 303 if (xhci_ep->isoch_enqueue == XHCI_ISOCH_BUFFER_COUNT - 2 && !xhci_ep->isoch_started) { 304 const uint8_t slot_id = xhci_dev->slot_id; 305 const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */ 306 err = hc_ring_doorbell(hc, slot_id, target); 307 if (err) { 308 return err; 309 } 310 xhci_ep->isoch_started = true; 311 } 312 313 /* Isochronous transfers don't handle errors, they skip them all. */ 314 transfer->batch.error = EOK; 315 transfer->batch.transfered_size = transfer->batch.buffer_size; 316 usb_transfer_batch_finish(&transfer->batch); 317 return EOK; 318 } 319 320 static int schedule_isochronous_in(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep, 321 xhci_device_t *xhci_dev) 322 { 323 /* If not yet started, start the isochronous endpoint transfers - before first read */ 324 if (!xhci_ep->isoch_started) { 325 const uint8_t slot_id = xhci_dev->slot_id; 326 const uint8_t target = xhci_endpoint_index(xhci_ep) + 1; /* EP Doorbells start at 1 */ 327 int err = hc_ring_doorbell(hc, slot_id, target); 328 if (err) { 329 return err; 330 } 331 xhci_ep->isoch_started = true; 332 } 333 334 // FIXME add some locks 335 xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_enqueue(xhci_ep); 336 while(!isoch_transfer) { 337 // FIXME: add condvar sleep; 338 } 339 340 isoch_transfer->size = transfer->batch.buffer_size; 341 if (transfer->batch.buffer_size <= isoch_transfer->size) { 342 if (transfer->batch.buffer_size > 0) { 343 memcpy(transfer->batch.buffer, isoch_transfer->data_virt, transfer->batch.buffer_size); 344 } 345 if (transfer->batch.buffer_size < isoch_transfer->size) { 346 // FIXME: somehow notify that buffer was too small, probably batch error code 347 } 348 transfer->batch.transfered_size = transfer->batch.buffer_size; 349 } 350 else { 351 memcpy(transfer->batch.buffer, isoch_transfer->data_virt, isoch_transfer->size); 352 transfer->batch.transfered_size = isoch_transfer->size; 353 } 354 355 // Clear and requeue the transfer with new TRB 356 xhci_trb_t trb; 357 xhci_trb_clean(&trb); 358 359 trb.parameter = isoch_transfer->data_phys; 360 isoch_transfer->size = xhci_ep->isoch_max_size; 361 362 xhci_trb_ring_t *ring = get_ring(hc, transfer); 363 int err = schedule_isochronous_trb(ring, xhci_ep, &trb, isoch_transfer->size, 364 &isoch_transfer->interrupt_trb_phys); 365 if (err) { 366 return err; 367 } 368 369 /* Isochronous transfers don't handle errors, they skip them all. */ 370 transfer->batch.error = EOK; 371 usb_transfer_batch_finish(&transfer->batch); 372 return EOK; 373 } 374 375 static int schedule_isochronous(xhci_hc_t* hc, xhci_transfer_t* transfer, xhci_endpoint_t *xhci_ep, 376 xhci_device_t *xhci_dev) 377 { 378 if (transfer->batch.buffer_size > xhci_ep->isoch_max_size) { 379 usb_log_error("Cannot schedule an oversized isochronous transfer."); 380 return EINVAL; 381 } 382 383 if (xhci_ep->base.direction == USB_DIRECTION_OUT) { 384 return schedule_isochronous_out(hc, transfer, xhci_ep, xhci_dev); 385 } 386 else { 387 return schedule_isochronous_in(hc, transfer, xhci_ep, xhci_dev); 388 } 389 } 390 391 static int handle_isochronous_transfer_event(xhci_hc_t *hc, xhci_trb_t *trb, xhci_endpoint_t *ep) { 392 fibril_mutex_lock(&ep->base.guard); 393 394 xhci_isoch_transfer_t *isoch_transfer = isoch_transfer_get_dequeue(ep); 395 if (isoch_transfer->interrupt_trb_phys != trb->parameter) { 396 usb_log_error("Non-matching trb to isochronous transfer, skipping."); 397 // FIXME: what to do? probably just kill the whole endpoint 398 return ENOENT; 399 } 400 401 if (ep->base.direction == USB_DIRECTION_IN) { 402 // We may have received less data, that's fine 403 isoch_transfer->size -= TRB_TRANSFER_LENGTH(*trb); 404 } 405 406 // TODO: Send notify to waiting fibrils in case they are waiting here 407 408 fibril_mutex_unlock(&ep->base.guard); 409 return EOK; 239 410 } 240 411 … … 262 433 ep->ring.dequeue = addr; 263 434 435 if (ep->base.transfer_type == USB_TRANSFER_ISOCHRONOUS) { 436 return handle_isochronous_transfer_event(hc, trb, ep); 437 } 438 264 439 fibril_mutex_lock(&ep->base.guard); 265 440 usb_transfer_batch_t *batch = ep->base.active_batch; … … 291 466 static const transfer_handler transfer_handlers[] = { 292 467 [USB_TRANSFER_CONTROL] = schedule_control, 293 [USB_TRANSFER_ISOCHRONOUS] = schedule_isochronous,468 [USB_TRANSFER_ISOCHRONOUS] = NULL, 294 469 [USB_TRANSFER_BULK] = schedule_bulk, 295 470 [USB_TRANSFER_INTERRUPT] = schedule_interrupt, … … 317 492 } 318 493 494 // Isochronous transfer needs to be handled differently 495 if(batch->ep->transfer_type == USB_TRANSFER_ISOCHRONOUS) { 496 return schedule_isochronous(hc, transfer, xhci_ep, xhci_dev); 497 } 498 319 499 const usb_transfer_type_t type = batch->ep->transfer_type; 320 500 assert(type >= 0 && type < ARRAY_SIZE(transfer_handlers)); -
uspace/drv/bus/usb/xhci/transfers.h
r888238e9 rd3086873 55 55 } xhci_transfer_t; 56 56 57 typedef struct { 58 /* Used buffer size */ 59 uint64_t size; 60 /* Pointer to data in virt memory */ 61 void *data_virt; 62 /* Physical address of the buffer */ 63 uintptr_t data_phys; 64 /* Physical address of enqueued TRB */ 65 uintptr_t interrupt_trb_phys; 66 } xhci_isoch_transfer_t; 67 57 68 xhci_transfer_t* xhci_transfer_create(endpoint_t *); 58 69 int xhci_transfer_schedule(xhci_hc_t *, usb_transfer_batch_t *);
Note:
See TracChangeset
for help on using the changeset viewer.