diff options
Diffstat (limited to 'drivers/usb/host/whci/qset.c')
-rw-r--r-- | drivers/usb/host/whci/qset.c | 831 |
1 files changed, 0 insertions, 831 deletions
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c deleted file mode 100644 index 925166a207aa..000000000000 --- a/drivers/usb/host/whci/qset.c +++ /dev/null @@ -1,831 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Wireless Host Controller (WHC) qset management. - * - * Copyright (C) 2007 Cambridge Silicon Radio Ltd. - */ -#include <linux/kernel.h> -#include <linux/dma-mapping.h> -#include <linux/slab.h> -#include <linux/uwb/umc.h> -#include <linux/usb.h> - -#include "../../wusbcore/wusbhc.h" - -#include "whcd.h" - -struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags) -{ - struct whc_qset *qset; - dma_addr_t dma; - - qset = dma_pool_zalloc(whc->qset_pool, mem_flags, &dma); - if (qset == NULL) - return NULL; - - qset->qset_dma = dma; - qset->whc = whc; - - INIT_LIST_HEAD(&qset->list_node); - INIT_LIST_HEAD(&qset->stds); - - return qset; -} - -/** - * qset_fill_qh - fill the static endpoint state in a qset's QHead - * @qset: the qset whose QH needs initializing with static endpoint - * state - * @urb: an urb for a transfer to this endpoint - */ -static void qset_fill_qh(struct whc *whc, struct whc_qset *qset, struct urb *urb) -{ - struct usb_device *usb_dev = urb->dev; - struct wusb_dev *wusb_dev = usb_dev->wusb_dev; - struct usb_wireless_ep_comp_descriptor *epcd; - bool is_out; - uint8_t phy_rate; - - is_out = usb_pipeout(urb->pipe); - - qset->max_packet = le16_to_cpu(urb->ep->desc.wMaxPacketSize); - - epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra; - if (epcd) { - qset->max_seq = epcd->bMaxSequence; - qset->max_burst = epcd->bMaxBurst; - } else { - qset->max_seq = 2; - qset->max_burst = 1; - } - - /* - * Initial PHY rate is 53.3 Mbit/s for control endpoints or - * the maximum supported by the device for other endpoints - * (unless limited by the user). - */ - if (usb_pipecontrol(urb->pipe)) - phy_rate = UWB_PHY_RATE_53; - else { - uint16_t phy_rates; - - phy_rates = le16_to_cpu(wusb_dev->wusb_cap_descr->wPHYRates); - phy_rate = fls(phy_rates) - 1; - if (phy_rate > whc->wusbhc.phy_rate) - phy_rate = whc->wusbhc.phy_rate; - } - - qset->qh.info1 = cpu_to_le32( - QH_INFO1_EP(usb_pipeendpoint(urb->pipe)) - | (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN) - | usb_pipe_to_qh_type(urb->pipe) - | QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum)) - | QH_INFO1_MAX_PKT_LEN(qset->max_packet) - ); - qset->qh.info2 = cpu_to_le32( - QH_INFO2_BURST(qset->max_burst) - | QH_INFO2_DBP(0) - | QH_INFO2_MAX_COUNT(3) - | QH_INFO2_MAX_RETRY(3) - | QH_INFO2_MAX_SEQ(qset->max_seq - 1) - ); - /* FIXME: where can we obtain these Tx parameters from? Why - * doesn't the chip know what Tx power to use? It knows the Rx - * strength and can presumably guess the Tx power required - * from that? */ - qset->qh.info3 = cpu_to_le32( - QH_INFO3_TX_RATE(phy_rate) - | QH_INFO3_TX_PWR(0) /* 0 == max power */ - ); - - qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1); -} - -/** - * qset_clear - clear fields in a qset so it may be reinserted into a - * schedule. - * - * The sequence number and current window are not cleared (see - * qset_reset()). - */ -void qset_clear(struct whc *whc, struct whc_qset *qset) -{ - qset->td_start = qset->td_end = qset->ntds = 0; - - qset->qh.link = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T); - qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK; - qset->qh.err_count = 0; - qset->qh.scratch[0] = 0; - qset->qh.scratch[1] = 0; - qset->qh.scratch[2] = 0; - - memset(&qset->qh.overlay, 0, sizeof(qset->qh.overlay)); - - init_completion(&qset->remove_complete); -} - -/** - * qset_reset - reset endpoint state in a qset. - * - * Clears the sequence number and current window. This qset must not - * be in the ASL or PZL. - */ -void qset_reset(struct whc *whc, struct whc_qset *qset) -{ - qset->reset = 0; - - qset->qh.status &= ~QH_STATUS_SEQ_MASK; - qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1); -} - -/** - * get_qset - get the qset for an async endpoint - * - * A new qset is created if one does not already exist. - */ -struct whc_qset *get_qset(struct whc *whc, struct urb *urb, - gfp_t mem_flags) -{ - struct whc_qset *qset; - - qset = urb->ep->hcpriv; - if (qset == NULL) { - qset = qset_alloc(whc, mem_flags); - if (qset == NULL) - return NULL; - - qset->ep = urb->ep; - urb->ep->hcpriv = qset; - qset_fill_qh(whc, qset, urb); - } - return qset; -} - -void qset_remove_complete(struct whc *whc, struct whc_qset *qset) -{ - qset->remove = 0; - list_del_init(&qset->list_node); - complete(&qset->remove_complete); -} - -/** - * qset_add_qtds - add qTDs for an URB to a qset - * - * Returns true if the list (ASL/PZL) must be updated because (for a - * WHCI 0.95 controller) an activated qTD was pointed to be iCur. - */ -enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset) -{ - struct whc_std *std; - enum whc_update update = 0; - - list_for_each_entry(std, &qset->stds, list_node) { - struct whc_qtd *qtd; - uint32_t status; - - if (qset->ntds >= WHCI_QSET_TD_MAX - || (qset->pause_after_urb && std->urb != qset->pause_after_urb)) - break; - - if (std->qtd) - continue; /* already has a qTD */ - - qtd = std->qtd = &qset->qtd[qset->td_end]; - - /* Fill in setup bytes for control transfers. */ - if (usb_pipecontrol(std->urb->pipe)) - memcpy(qtd->setup, std->urb->setup_packet, 8); - - status = QTD_STS_ACTIVE | QTD_STS_LEN(std->len); - - if (whc_std_last(std) && usb_pipeout(std->urb->pipe)) - status |= QTD_STS_LAST_PKT; - - /* - * For an IN transfer the iAlt field should be set so - * the h/w will automatically advance to the next - * transfer. However, if there are 8 or more TDs - * remaining in this transfer then iAlt cannot be set - * as it could point to somewhere in this transfer. - */ - if (std->ntds_remaining < WHCI_QSET_TD_MAX) { - int ialt; - ialt = (qset->td_end + std->ntds_remaining) % WHCI_QSET_TD_MAX; - status |= QTD_STS_IALT(ialt); - } else if (usb_pipein(std->urb->pipe)) - qset->pause_after_urb = std->urb; - - if (std->num_pointers) - qtd->options = cpu_to_le32(QTD_OPT_IOC); - else - qtd->options = cpu_to_le32(QTD_OPT_IOC | QTD_OPT_SMALL); - qtd->page_list_ptr = cpu_to_le64(std->dma_addr); - - qtd->status = cpu_to_le32(status); - - if (QH_STATUS_TO_ICUR(qset->qh.status) == qset->td_end) - update = WHC_UPDATE_UPDATED; - - if (++qset->td_end >= WHCI_QSET_TD_MAX) - qset->td_end = 0; - qset->ntds++; - } - - return update; -} - -/** - * qset_remove_qtd - remove the first qTD from a qset. - * - * The qTD might be still active (if it's part of a IN URB that - * resulted in a short read) so ensure it's deactivated. - */ -static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset) -{ - qset->qtd[qset->td_start].status = 0; - - if (++qset->td_start >= WHCI_QSET_TD_MAX) - qset->td_start = 0; - qset->ntds--; -} - -static void qset_copy_bounce_to_sg(struct whc *whc, struct whc_std *std) -{ - struct scatterlist *sg; - void *bounce; - size_t remaining, offset; - - bounce = std->bounce_buf; - remaining = std->len; - - sg = std->bounce_sg; - offset = std->bounce_offset; - - while (remaining) { - size_t len; - - len = min(sg->length - offset, remaining); - memcpy(sg_virt(sg) + offset, bounce, len); - - bounce += len; - remaining -= len; - - offset += len; - if (offset >= sg->length) { - sg = sg_next(sg); - offset = 0; - } - } - -} - -/** - * qset_free_std - remove an sTD and free it. - * @whc: the WHCI host controller - * @std: the sTD to remove and free. - */ -void qset_free_std(struct whc *whc, struct whc_std *std) -{ - list_del(&std->list_node); - if (std->bounce_buf) { - bool is_out = usb_pipeout(std->urb->pipe); - dma_addr_t dma_addr; - - if (std->num_pointers) - dma_addr = le64_to_cpu(std->pl_virt[0].buf_ptr); - else - dma_addr = std->dma_addr; - - dma_unmap_single(whc->wusbhc.dev, dma_addr, - std->len, is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (!is_out) - qset_copy_bounce_to_sg(whc, std); - kfree(std->bounce_buf); - } - if (std->pl_virt) { - if (!dma_mapping_error(whc->wusbhc.dev, std->dma_addr)) - dma_unmap_single(whc->wusbhc.dev, std->dma_addr, - std->num_pointers * sizeof(struct whc_page_list_entry), - DMA_TO_DEVICE); - kfree(std->pl_virt); - std->pl_virt = NULL; - } - kfree(std); -} - -/** - * qset_remove_qtds - remove an URB's qTDs (and sTDs). - */ -static void qset_remove_qtds(struct whc *whc, struct whc_qset *qset, - struct urb *urb) -{ - struct whc_std *std, *t; - - list_for_each_entry_safe(std, t, &qset->stds, list_node) { - if (std->urb != urb) - break; - if (std->qtd != NULL) - qset_remove_qtd(whc, qset); - qset_free_std(whc, std); - } -} - -/** - * qset_free_stds - free any remaining sTDs for an URB. - */ -static void qset_free_stds(struct whc_qset *qset, struct urb *urb) -{ - struct whc_std *std, *t; - - list_for_each_entry_safe(std, t, &qset->stds, list_node) { - if (std->urb == urb) - qset_free_std(qset->whc, std); - } -} - -static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_flags) -{ - dma_addr_t dma_addr = std->dma_addr; - dma_addr_t sp, ep; - size_t pl_len; - int p; - - /* Short buffers don't need a page list. */ - if (std->len <= WHCI_PAGE_SIZE) { - std->num_pointers = 0; - return 0; - } - - sp = dma_addr & ~(WHCI_PAGE_SIZE-1); - ep = dma_addr + std->len; - std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); - - pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); - std->pl_virt = kmalloc(pl_len, mem_flags); - if (std->pl_virt == NULL) - return -ENOMEM; - std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, pl_len, DMA_TO_DEVICE); - if (dma_mapping_error(whc->wusbhc.dev, std->dma_addr)) { - kfree(std->pl_virt); - return -EFAULT; - } - - for (p = 0; p < std->num_pointers; p++) { - std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); - dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); - } - - return 0; -} - -/** - * urb_dequeue_work - executes asl/pzl update and gives back the urb to the system. - */ -static void urb_dequeue_work(struct work_struct *work) -{ - struct whc_urb *wurb = container_of(work, struct whc_urb, dequeue_work); - struct whc_qset *qset = wurb->qset; - struct whc *whc = qset->whc; - unsigned long flags; - - if (wurb->is_async) - asl_update(whc, WUSBCMD_ASYNC_UPDATED - | WUSBCMD_ASYNC_SYNCED_DB - | WUSBCMD_ASYNC_QSET_RM); - else - pzl_update(whc, WUSBCMD_PERIODIC_UPDATED - | WUSBCMD_PERIODIC_SYNCED_DB - | WUSBCMD_PERIODIC_QSET_RM); - - spin_lock_irqsave(&whc->lock, flags); - qset_remove_urb(whc, qset, wurb->urb, wurb->status); - spin_unlock_irqrestore(&whc->lock, flags); -} - -static struct whc_std *qset_new_std(struct whc *whc, struct whc_qset *qset, - struct urb *urb, gfp_t mem_flags) -{ - struct whc_std *std; - - std = kzalloc(sizeof(struct whc_std), mem_flags); - if (std == NULL) - return NULL; - - std->urb = urb; - std->qtd = NULL; - - INIT_LIST_HEAD(&std->list_node); - list_add_tail(&std->list_node, &qset->stds); - - return std; -} - -static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *urb, - gfp_t mem_flags) -{ - size_t remaining; - struct scatterlist *sg; - int i; - int ntds = 0; - struct whc_std *std = NULL; - struct whc_page_list_entry *new_pl_virt; - dma_addr_t prev_end = 0; - size_t pl_len; - int p = 0; - - remaining = urb->transfer_buffer_length; - - for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) { - dma_addr_t dma_addr; - size_t dma_remaining; - dma_addr_t sp, ep; - int num_pointers; - - if (remaining == 0) { - break; - } - - dma_addr = sg_dma_address(sg); - dma_remaining = min_t(size_t, sg_dma_len(sg), remaining); - - while (dma_remaining) { - size_t dma_len; - - /* - * We can use the previous std (if it exists) provided that: - * - the previous one ended on a page boundary. - * - the current one begins on a page boundary. - * - the previous one isn't full. - * - * If a new std is needed but the previous one - * was not a whole number of packets then this - * sg list cannot be mapped onto multiple - * qTDs. Return an error and let the caller - * sort it out. - */ - if (!std - || (prev_end & (WHCI_PAGE_SIZE-1)) - || (dma_addr & (WHCI_PAGE_SIZE-1)) - || std->len + WHCI_PAGE_SIZE > QTD_MAX_XFER_SIZE) { - if (std && std->len % qset->max_packet != 0) - return -EINVAL; - std = qset_new_std(whc, qset, urb, mem_flags); - if (std == NULL) { - return -ENOMEM; - } - ntds++; - p = 0; - } - - dma_len = dma_remaining; - - /* - * If the remainder of this element doesn't - * fit in a single qTD, limit the qTD to a - * whole number of packets. This allows the - * remainder to go into the next qTD. - */ - if (std->len + dma_len > QTD_MAX_XFER_SIZE) { - dma_len = (QTD_MAX_XFER_SIZE / qset->max_packet) - * qset->max_packet - std->len; - } - - std->len += dma_len; - std->ntds_remaining = -1; /* filled in later */ - - sp = dma_addr & ~(WHCI_PAGE_SIZE-1); - ep = dma_addr + dma_len; - num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE); - std->num_pointers += num_pointers; - - pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); - - new_pl_virt = krealloc(std->pl_virt, pl_len, mem_flags); - if (new_pl_virt == NULL) { - kfree(std->pl_virt); - std->pl_virt = NULL; - return -ENOMEM; - } - std->pl_virt = new_pl_virt; - - for (;p < std->num_pointers; p++) { - std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr); - dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1); - } - - prev_end = dma_addr = ep; - dma_remaining -= dma_len; - remaining -= dma_len; - } - } - - /* Now the number of stds is know, go back and fill in - std->ntds_remaining. */ - list_for_each_entry(std, &qset->stds, list_node) { - if (std->ntds_remaining == -1) { - pl_len = std->num_pointers * sizeof(struct whc_page_list_entry); - std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, - pl_len, DMA_TO_DEVICE); - if (dma_mapping_error(whc->wusbhc.dev, std->dma_addr)) - return -EFAULT; - std->ntds_remaining = ntds--; - } - } - return 0; -} - -/** - * qset_add_urb_sg_linearize - add an urb with sg list, copying the data - * - * If the URB contains an sg list whose elements cannot be directly - * mapped to qTDs then the data must be transferred via bounce - * buffers. - */ -static int qset_add_urb_sg_linearize(struct whc *whc, struct whc_qset *qset, - struct urb *urb, gfp_t mem_flags) -{ - bool is_out = usb_pipeout(urb->pipe); - size_t max_std_len; - size_t remaining; - int ntds = 0; - struct whc_std *std = NULL; - void *bounce = NULL; - struct scatterlist *sg; - int i; - - /* limit maximum bounce buffer to 16 * 3.5 KiB ~= 28 k */ - max_std_len = qset->max_burst * qset->max_packet; - - remaining = urb->transfer_buffer_length; - - for_each_sg(urb->sg, sg, urb->num_mapped_sgs, i) { - size_t len; - size_t sg_remaining; - void *orig; - - if (remaining == 0) { - break; - } - - sg_remaining = min_t(size_t, remaining, sg->length); - orig = sg_virt(sg); - - while (sg_remaining) { - if (!std || std->len == max_std_len) { - std = qset_new_std(whc, qset, urb, mem_flags); - if (std == NULL) - return -ENOMEM; - std->bounce_buf = kmalloc(max_std_len, mem_flags); - if (std->bounce_buf == NULL) - return -ENOMEM; - std->bounce_sg = sg; - std->bounce_offset = orig - sg_virt(sg); - bounce = std->bounce_buf; - ntds++; - } - - len = min(sg_remaining, max_std_len - std->len); - - if (is_out) - memcpy(bounce, orig, len); - - std->len += len; - std->ntds_remaining = -1; /* filled in later */ - - bounce += len; - orig += len; - sg_remaining -= len; - remaining -= len; - } - } - - /* - * For each of the new sTDs, map the bounce buffers, create - * page lists (if necessary), and fill in std->ntds_remaining. - */ - list_for_each_entry(std, &qset->stds, list_node) { - if (std->ntds_remaining != -1) - continue; - - std->dma_addr = dma_map_single(&whc->umc->dev, std->bounce_buf, std->len, - is_out ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (dma_mapping_error(&whc->umc->dev, std->dma_addr)) - return -EFAULT; - - if (qset_fill_page_list(whc, std, mem_flags) < 0) - return -ENOMEM; - - std->ntds_remaining = ntds--; - } - - return 0; -} - -/** - * qset_add_urb - add an urb to the qset's queue. - * - * The URB is chopped into sTDs, one for each qTD that will required. - * At least one qTD (and sTD) is required even if the transfer has no - * data (e.g., for some control transfers). - */ -int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb, - gfp_t mem_flags) -{ - struct whc_urb *wurb; - int remaining = urb->transfer_buffer_length; - u64 transfer_dma = urb->transfer_dma; - int ntds_remaining; - int ret; - - wurb = kzalloc(sizeof(struct whc_urb), mem_flags); - if (wurb == NULL) - goto err_no_mem; - urb->hcpriv = wurb; - wurb->qset = qset; - wurb->urb = urb; - INIT_WORK(&wurb->dequeue_work, urb_dequeue_work); - - if (urb->num_sgs) { - ret = qset_add_urb_sg(whc, qset, urb, mem_flags); - if (ret == -EINVAL) { - qset_free_stds(qset, urb); - ret = qset_add_urb_sg_linearize(whc, qset, urb, mem_flags); - } - if (ret < 0) - goto err_no_mem; - return 0; - } - - ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE); - if (ntds_remaining == 0) - ntds_remaining = 1; - - while (ntds_remaining) { - struct whc_std *std; - size_t std_len; - - std_len = remaining; - if (std_len > QTD_MAX_XFER_SIZE) - std_len = QTD_MAX_XFER_SIZE; - - std = qset_new_std(whc, qset, urb, mem_flags); - if (std == NULL) - goto err_no_mem; - - std->dma_addr = transfer_dma; - std->len = std_len; - std->ntds_remaining = ntds_remaining; - - if (qset_fill_page_list(whc, std, mem_flags) < 0) - goto err_no_mem; - - ntds_remaining--; - remaining -= std_len; - transfer_dma += std_len; - } - - return 0; - -err_no_mem: - qset_free_stds(qset, urb); - return -ENOMEM; -} - -/** - * qset_remove_urb - remove an URB from the urb queue. - * - * The URB is returned to the USB subsystem. - */ -void qset_remove_urb(struct whc *whc, struct whc_qset *qset, - struct urb *urb, int status) -{ - struct wusbhc *wusbhc = &whc->wusbhc; - struct whc_urb *wurb = urb->hcpriv; - - usb_hcd_unlink_urb_from_ep(&wusbhc->usb_hcd, urb); - /* Drop the lock as urb->complete() may enqueue another urb. */ - spin_unlock(&whc->lock); - wusbhc_giveback_urb(wusbhc, urb, status); - spin_lock(&whc->lock); - - kfree(wurb); -} - -/** - * get_urb_status_from_qtd - get the completed urb status from qTD status - * @urb: completed urb - * @status: qTD status - */ -static int get_urb_status_from_qtd(struct urb *urb, u32 status) -{ - if (status & QTD_STS_HALTED) { - if (status & QTD_STS_DBE) - return usb_pipein(urb->pipe) ? -ENOSR : -ECOMM; - else if (status & QTD_STS_BABBLE) - return -EOVERFLOW; - else if (status & QTD_STS_RCE) - return -ETIME; - return -EPIPE; - } - if (usb_pipein(urb->pipe) - && (urb->transfer_flags & URB_SHORT_NOT_OK) - && urb->actual_length < urb->transfer_buffer_length) - return -EREMOTEIO; - return 0; -} - -/** - * process_inactive_qtd - process an inactive (but not halted) qTD. - * - * Update the urb with the transfer bytes from the qTD, if the urb is - * completely transferred or (in the case of an IN only) the LPF is - * set, then the transfer is complete and the urb should be returned - * to the system. - */ -void process_inactive_qtd(struct whc *whc, struct whc_qset *qset, - struct whc_qtd *qtd) -{ - struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node); - struct urb *urb = std->urb; - uint32_t status; - bool complete; - - status = le32_to_cpu(qtd->status); - - urb->actual_length += std->len - QTD_STS_TO_LEN(status); - - if (usb_pipein(urb->pipe) && (status & QTD_STS_LAST_PKT)) - complete = true; - else - complete = whc_std_last(std); - - qset_remove_qtd(whc, qset); - qset_free_std(whc, std); - - /* - * Transfers for this URB are complete? Then return it to the - * USB subsystem. - */ - if (complete) { - qset_remove_qtds(whc, qset, urb); - qset_remove_urb(whc, qset, urb, get_urb_status_from_qtd(urb, status)); - - /* - * If iAlt isn't valid then the hardware didn't - * advance iCur. Adjust the start and end pointers to - * match iCur. - */ - if (!(status & QTD_STS_IALT_VALID)) - qset->td_start = qset->td_end - = QH_STATUS_TO_ICUR(le16_to_cpu(qset->qh.status)); - qset->pause_after_urb = NULL; - } -} - -/** - * process_halted_qtd - process a qset with a halted qtd - * - * Remove all the qTDs for the failed URB and return the failed URB to - * the USB subsystem. Then remove all other qTDs so the qset can be - * removed. - * - * FIXME: this is the point where rate adaptation can be done. If a - * transfer failed because it exceeded the maximum number of retries - * then it could be reactivated with a slower rate without having to - * remove the qset. - */ -void process_halted_qtd(struct whc *whc, struct whc_qset *qset, - struct whc_qtd *qtd) -{ - struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node); - struct urb *urb = std->urb; - int urb_status; - - urb_status = get_urb_status_from_qtd(urb, le32_to_cpu(qtd->status)); - - qset_remove_qtds(whc, qset, urb); - qset_remove_urb(whc, qset, urb, urb_status); - - list_for_each_entry(std, &qset->stds, list_node) { - if (qset->ntds == 0) - break; - qset_remove_qtd(whc, qset); - std->qtd = NULL; - } - - qset->remove = 1; -} - -void qset_free(struct whc *whc, struct whc_qset *qset) -{ - dma_pool_free(whc->qset_pool, qset, qset->qset_dma); -} - -/** - * qset_delete - wait for a qset to be unused, then free it. - */ -void qset_delete(struct whc *whc, struct whc_qset *qset) -{ - wait_for_completion(&qset->remove_complete); - qset_free(whc, qset); -} |