Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: whci-hcd: support urbs with scatter-gather lists

Support urbs with scatter-gather lists by trying to fit sg list elements
into page lists in one or more qTDs. qTDs must end on a wMaxPacketSize
boundary so if this isn't possible the urb's sg list must be copied into
bounce buffers.

Signed-off-by: David Vrabel <david.vrabel@csr.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

David Vrabel and committed by
Greg Kroah-Hartman
294a39e7 4c1bd3d7

+333 -32
+1
drivers/usb/host/whci/hcd.c
··· 250 250 } 251 251 252 252 usb_hcd->wireless = 1; 253 + usb_hcd->self.sg_tablesize = 2048; /* somewhat arbitrary */ 253 254 254 255 wusbhc = usb_hcd_to_wusbhc(usb_hcd); 255 256 whc = wusbhc_to_whc(wusbhc);
+320 -30
drivers/usb/host/whci/qset.c
··· 57 57 58 58 is_out = usb_pipeout(urb->pipe); 59 59 60 - epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra; 60 + qset->max_packet = le16_to_cpu(urb->ep->desc.wMaxPacketSize); 61 61 62 + epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra; 62 63 if (epcd) { 63 64 qset->max_seq = epcd->bMaxSequence; 64 65 qset->max_burst = epcd->bMaxBurst; ··· 73 72 | (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN) 74 73 | usb_pipe_to_qh_type(urb->pipe) 75 74 | QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum)) 76 - | QH_INFO1_MAX_PKT_LEN(usb_maxpacket(urb->dev, urb->pipe, is_out)) 75 + | QH_INFO1_MAX_PKT_LEN(qset->max_packet) 77 76 ); 78 77 qset->qh.info2 = cpu_to_le32( 79 78 QH_INFO2_BURST(qset->max_burst) ··· 242 241 qset->ntds--; 243 242 } 244 243 244 + static void qset_copy_bounce_to_sg(struct whc *whc, struct whc_std *std) 245 + { 246 + struct scatterlist *sg; 247 + void *bounce; 248 + size_t remaining, offset; 249 + 250 + bounce = std->bounce_buf; 251 + remaining = std->len; 252 + 253 + sg = std->bounce_sg; 254 + offset = std->bounce_offset; 255 + 256 + while (remaining) { 257 + size_t len; 258 + 259 + len = min(sg->length - offset, remaining); 260 + memcpy(sg_virt(sg) + offset, bounce, len); 261 + 262 + bounce += len; 263 + remaining -= len; 264 + 265 + offset += len; 266 + if (offset >= sg->length) { 267 + sg = sg_next(sg); 268 + offset = 0; 269 + } 270 + } 271 + 272 + } 273 + 245 274 /** 246 275 * qset_free_std - remove an sTD and free it. 247 276 * @whc: the WHCI host controller ··· 280 249 void qset_free_std(struct whc *whc, struct whc_std *std) 281 250 { 282 251 list_del(&std->list_node); 283 - if (std->num_pointers) { 284 - dma_unmap_single(whc->wusbhc.dev, std->dma_addr, 285 - std->num_pointers * sizeof(struct whc_page_list_entry), 286 - DMA_TO_DEVICE); 287 - kfree(std->pl_virt); 288 - } 252 + if (std->bounce_buf) { 253 + bool is_out = usb_pipeout(std->urb->pipe); 254 + dma_addr_t dma_addr; 289 255 256 + if (std->num_pointers) 257 + dma_addr = le64_to_cpu(std->pl_virt[0].buf_ptr); 258 + else 259 + dma_addr = std->dma_addr; 260 + 261 + dma_unmap_single(whc->wusbhc.dev, dma_addr, 262 + std->len, is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE); 263 + if (!is_out) 264 + qset_copy_bounce_to_sg(whc, std); 265 + kfree(std->bounce_buf); 266 + } 267 + if (std->pl_virt) { 268 + if (std->dma_addr) 269 + dma_unmap_single(whc->wusbhc.dev, std->dma_addr, 270 + std->num_pointers * sizeof(struct whc_page_list_entry), 271 + DMA_TO_DEVICE); 272 + kfree(std->pl_virt); 273 + std->pl_virt = NULL; 274 + } 290 275 kfree(std); 291 276 } 292 277 ··· 340 293 { 341 294 dma_addr_t dma_addr = std->dma_addr; 342 295 dma_addr_t sp, ep; 343 - size_t std_len = std->len; 344 296 size_t pl_len; 345 297 int p; 346 298 347 - sp = ALIGN(dma_addr, WHCI_PAGE_SIZE); 348 - ep = dma_addr + std_len; 299 + /* Short buffers don't need a page list. */ 300 + if (std->len <= WHCI_PAGE_SIZE) { 301 + std->num_pointers = 0; 302 + return 0; 303 + } 304 + 305 + sp = dma_addr & ~(WHCI_PAGE_SIZE-1); 306 + ep = dma_addr + std->len; 349 307 std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); 350 308 351 309 pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); ··· 361 309 362 310 for (p = 0; p < std->num_pointers; p++) { 363 311 std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); 364 - dma_addr = ALIGN(dma_addr + WHCI_PAGE_SIZE, WHCI_PAGE_SIZE); 312 + dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); 365 313 } 366 314 367 315 return 0; ··· 391 339 spin_unlock_irqrestore(&whc->lock, flags); 392 340 } 393 341 342 + static struct whc_std *qset_new_std(struct whc *whc, struct whc_qset *qset, 343 + struct urb *urb, gfp_t mem_flags) 344 + { 345 + struct whc_std *std; 346 + 347 + std = kzalloc(sizeof(struct whc_std), mem_flags); 348 + if (std == NULL) 349 + return NULL; 350 + 351 + std->urb = urb; 352 + std->qtd = NULL; 353 + 354 + INIT_LIST_HEAD(&std->list_node); 355 + list_add_tail(&std->list_node, &qset->stds); 356 + 357 + return std; 358 + } 359 + 360 + static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *urb, 361 + gfp_t mem_flags) 362 + { 363 + size_t remaining; 364 + struct scatterlist *sg; 365 + int i; 366 + int ntds = 0; 367 + struct whc_std *std = NULL; 368 + struct whc_page_list_entry *entry; 369 + dma_addr_t prev_end = 0; 370 + size_t pl_len; 371 + int p = 0; 372 + 373 + dev_dbg(&whc->umc->dev, "adding urb w/ sg of length %d\n", urb->transfer_buffer_length); 374 + 375 + remaining = urb->transfer_buffer_length; 376 + 377 + for_each_sg(urb->sg->sg, sg, urb->num_sgs, i) { 378 + dma_addr_t dma_addr; 379 + size_t dma_remaining; 380 + dma_addr_t sp, ep; 381 + int num_pointers; 382 + 383 + if (remaining == 0) { 384 + break; 385 + } 386 + 387 + dma_addr = sg_dma_address(sg); 388 + dma_remaining = min(sg_dma_len(sg), remaining); 389 + 390 + dev_dbg(&whc->umc->dev, "adding sg[%d] %08x %d\n", i, (unsigned)dma_addr, 391 + dma_remaining); 392 + 393 + while (dma_remaining) { 394 + size_t dma_len; 395 + 396 + /* 397 + * We can use the previous std (if it exists) provided that: 398 + * - the previous one ended on a page boundary. 399 + * - the current one begins on a page boundary. 400 + * - the previous one isn't full. 401 + * 402 + * If a new std is needed but the previous one 403 + * did not end on a wMaxPacketSize boundary 404 + * then this sg list cannot be mapped onto 405 + * multiple qTDs. Return an error and let the 406 + * caller sort it out. 407 + */ 408 + if (!std 409 + || (prev_end & (WHCI_PAGE_SIZE-1)) 410 + || (dma_addr & (WHCI_PAGE_SIZE-1)) 411 + || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) { 412 + if (prev_end % qset->max_packet != 0) 413 + return -EINVAL; 414 + dev_dbg(&whc->umc->dev, "need new std\n"); 415 + std = qset_new_std(whc, qset, urb, mem_flags); 416 + if (std == NULL) { 417 + return -ENOMEM; 418 + } 419 + ntds++; 420 + p = 0; 421 + } 422 + 423 + dma_len = dma_remaining; 424 + 425 + /* 426 + * If the remainder in this element doesn't 427 + * fit in a single qTD, end the qTD on a 428 + * wMaxPacketSize boundary. 429 + */ 430 + if (std->len + dma_len > QTD_MAX_XFER_SIZE) { 431 + dma_len = QTD_MAX_XFER_SIZE - std->len; 432 + ep = ((dma_addr + dma_len) / qset->max_packet) * qset->max_packet; 433 + dma_len = ep - dma_addr; 434 + } 435 + 436 + dev_dbg(&whc->umc->dev, "adding %d\n", dma_len); 437 + 438 + std->len += dma_len; 439 + std->ntds_remaining = -1; /* filled in later */ 440 + 441 + sp = dma_addr & ~(WHCI_PAGE_SIZE-1); 442 + ep = dma_addr + dma_len; 443 + num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); 444 + std->num_pointers += num_pointers; 445 + 446 + dev_dbg(&whc->umc->dev, "need %d more (%d total) page pointers\n", 447 + num_pointers, std->num_pointers); 448 + 449 + pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); 450 + 451 + std->pl_virt = krealloc(std->pl_virt, pl_len, mem_flags); 452 + if (std->pl_virt == NULL) { 453 + return -ENOMEM; 454 + } 455 + 456 + for (;p < std->num_pointers; p++, entry++) { 457 + dev_dbg(&whc->umc->dev, "e[%d] %08x\n", p, dma_addr); 458 + std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); 459 + dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); 460 + } 461 + 462 + prev_end = dma_addr = ep; 463 + dma_remaining -= dma_len; 464 + remaining -= dma_len; 465 + } 466 + } 467 + 468 + dev_dbg(&whc->umc->dev, "used %d tds\n", ntds); 469 + 470 + /* Now the number of stds is know, go back and fill in 471 + std->ntds_remaining. */ 472 + list_for_each_entry(std, &qset->stds, list_node) { 473 + if (std->ntds_remaining == -1) { 474 + pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); 475 + std->ntds_remaining = ntds--; 476 + std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, 477 + pl_len, DMA_TO_DEVICE); 478 + } 479 + } 480 + return 0; 481 + } 482 + 483 + /** 484 + * qset_add_urb_sg_linearize - add an urb with sg list, copying the data 485 + * 486 + * If the URB contains an sg list whose elements cannot be directly 487 + * mapped to qTDs then the data must be transferred via bounce 488 + * buffers. 489 + */ 490 + static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset, 491 + struct urb *urb, gfp_t mem_flags) 492 + { 493 + bool is_out = usb_pipeout(urb->pipe); 494 + size_t max_std_len; 495 + size_t remaining; 496 + int ntds = 0; 497 + struct whc_std *std = NULL; 498 + void *bounce = NULL; 499 + struct scatterlist *sg; 500 + int i; 501 + 502 + /* limit maximum bounce buffer to 16 * 3.5 KiB ~= 28 k */ 503 + max_std_len = qset->max_burst * qset->max_packet; 504 + 505 + remaining = urb->transfer_buffer_length; 506 + 507 + for_each_sg(urb->sg->sg, sg, urb->sg->nents, i) { 508 + size_t len; 509 + size_t sg_remaining; 510 + void *orig; 511 + 512 + if (remaining == 0) { 513 + break; 514 + } 515 + 516 + sg_remaining = min(remaining, sg->length); 517 + orig = sg_virt(sg); 518 + 519 + dev_dbg(&whc->umc->dev, "adding sg[%d] %d\n", i, sg_remaining); 520 + 521 + while (sg_remaining) { 522 + if (!std || std->len == max_std_len) { 523 + dev_dbg(&whc->umc->dev, "need new std\n"); 524 + std = qset_new_std(whc, qset, urb, mem_flags); 525 + if (std == NULL) 526 + return -ENOMEM; 527 + std->bounce_buf = kmalloc(max_std_len, mem_flags); 528 + if (std->bounce_buf == NULL) 529 + return -ENOMEM; 530 + std->bounce_sg = sg; 531 + std->bounce_offset = orig - sg_virt(sg); 532 + bounce = std->bounce_buf; 533 + ntds++; 534 + } 535 + 536 + len = min(sg_remaining, max_std_len - std->len); 537 + 538 + dev_dbg(&whc->umc->dev, "added %d from sg[%d] @ offset %d\n", 539 + len, i, orig - sg_virt(sg)); 540 + 541 + if (is_out) 542 + memcpy(bounce, orig, len); 543 + 544 + std->len += len; 545 + std->ntds_remaining = -1; /* filled in later */ 546 + 547 + bounce += len; 548 + orig += len; 549 + sg_remaining -= len; 550 + remaining -= len; 551 + } 552 + } 553 + 554 + /* 555 + * For each of the new sTDs, map the bounce buffers, create 556 + * page lists (if necessary), and fill in std->ntds_remaining. 557 + */ 558 + list_for_each_entry(std, &qset->stds, list_node) { 559 + if (std->ntds_remaining != -1) 560 + continue; 561 + 562 + std->dma_addr = dma_map_single(&whc->umc->dev, std->bounce_buf, std->len, 563 + is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE); 564 + 565 + if (qset_fill_page_list(whc, std, mem_flags) < 0) 566 + return -ENOMEM; 567 + 568 + std->ntds_remaining = ntds--; 569 + } 570 + 571 + return 0; 572 + } 573 + 394 574 /** 395 575 * qset_add_urb - add an urb to the qset's queue. 396 576 * ··· 637 353 int remaining = urb->transfer_buffer_length; 638 354 u64 transfer_dma = urb->transfer_dma; 639 355 int ntds_remaining; 640 - 641 - ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE); 642 - if (ntds_remaining == 0) 643 - ntds_remaining = 1; 356 + int ret; 644 357 645 358 wurb = kzalloc(sizeof(struct whc_urb), mem_flags); 646 359 if (wurb == NULL) ··· 647 366 wurb->urb = urb; 648 367 INIT_WORK(&wurb->dequeue_work, urb_dequeue_work); 649 368 369 + if (urb->sg) { 370 + ret = qset_add_urb_sg(whc, qset, urb, mem_flags); 371 + if (ret == -EINVAL) { 372 + dev_dbg(&whc->umc->dev, "linearizing %d octet urb\n", 373 + urb->transfer_buffer_length); 374 + qset_free_stds(qset, urb); 375 + ret = qset_add_urb_sg_linearize(whc, qset, urb, mem_flags); 376 + } 377 + if (ret < 0) 378 + goto err_no_mem; 379 + return 0; 380 + } 381 + 382 + ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE); 383 + if (ntds_remaining == 0) 384 + ntds_remaining = 1; 385 + 650 386 while (ntds_remaining) { 651 387 struct whc_std *std; 652 388 size_t std_len; 653 - 654 - std = kmalloc(sizeof(struct whc_std), mem_flags); 655 - if (std == NULL) 656 - goto err_no_mem; 657 389 658 390 std_len = remaining; 659 391 if (std_len > QTD_MAX_XFER_SIZE) 660 392 std_len = QTD_MAX_XFER_SIZE; 661 393 662 - std->urb = urb; 394 + std = qset_new_std(whc, qset, urb, mem_flags); 395 + if (std == NULL) 396 + goto err_no_mem; 397 + 663 398 std->dma_addr = transfer_dma; 664 399 std->len = std_len; 665 400 std->ntds_remaining = ntds_remaining; 666 - std->qtd = NULL; 667 401 668 - INIT_LIST_HEAD(&std->list_node); 669 - list_add_tail(&std->list_node, &qset->stds); 670 - 671 - if (std_len > WHCI_PAGE_SIZE) { 672 - if (qset_fill_page_list(whc, std, mem_flags) < 0) 673 - goto err_no_mem; 674 - } else 675 - std->num_pointers = 0; 402 + if (qset_fill_page_list(whc, std, mem_flags) < 0) 403 + goto err_no_mem; 676 404 677 405 ntds_remaining--; 678 406 remaining -= std_len;
+9
drivers/usb/host/whci/whcd.h
··· 84 84 * @len: the length of data in the associated TD. 85 85 * @ntds_remaining: number of TDs (starting from this one) in this transfer. 86 86 * 87 + * @bounce_buf: a bounce buffer if the std was from an urb with a sg 88 + * list that could not be mapped to qTDs directly. 89 + * @bounce_sg: the first scatterlist element bounce_buf is for. 90 + * @bounce_offset: the offset into bounce_sg for the start of bounce_buf. 91 + * 87 92 * Queued URBs may require more TDs than are available in a qset so we 88 93 * use a list of these "software TDs" (sTDs) to hold per-TD data. 89 94 */ ··· 102 97 int num_pointers; 103 98 dma_addr_t dma_addr; 104 99 struct whc_page_list_entry *pl_virt; 100 + 101 + void *bounce_buf; 102 + struct scatterlist *bounce_sg; 103 + unsigned bounce_offset; 105 104 }; 106 105 107 106 /**
+3 -2
drivers/usb/host/whci/whci-hc.h
··· 267 267 unsigned reset:1; 268 268 struct urb *pause_after_urb; 269 269 struct completion remove_complete; 270 - int max_burst; 271 - int max_seq; 270 + uint16_t max_packet; 271 + uint8_t max_burst; 272 + uint8_t max_seq; 272 273 }; 273 274 274 275 static inline void whc_qset_set_link_ptr(u64 *ptr, u64 target)