?? uhci-q.c
字號:
/* * Build the new dummy TD and activate the old one */ td = uhci_alloc_td(uhci); if (!td) goto nomem; *plink = LINK_TO_TD(td); uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); wmb(); qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE); qh->dummy_td = td; /* Low-speed transfers get a different queue, and won't hog the bus. * Also, some devices enumerate better without FSBR; the easiest way * to do that is to put URBs on the low-speed queue while the device * isn't in the CONFIGURED state. */ if (urb->dev->speed == USB_SPEED_LOW || urb->dev->state != USB_STATE_CONFIGURED) skel = SKEL_LS_CONTROL; else { skel = SKEL_FS_CONTROL; uhci_add_fsbr(uhci, urb); } if (qh->state != QH_STATE_ACTIVE) qh->skel = skel; urb->actual_length = -8; /* Account for the SETUP packet */ return 0;nomem: /* Remove the dummy TD from the td_list so it doesn't get freed */ uhci_remove_td_from_urbp(qh->dummy_td); return -ENOMEM;}/* * Common submit for bulk and interrupt */static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct uhci_qh *qh){ struct uhci_td *td; unsigned long destination, status; int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; __le32 *plink; struct urb_priv *urbp = urb->hcpriv; unsigned int toggle; if (len < 0) return -EINVAL; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); /* 3 errors, dummy TD remains inactive */ status = uhci_maxerr(3); if (urb->dev->speed == USB_SPEED_LOW) status |= TD_CTRL_LS; if (usb_pipein(urb->pipe)) status |= TD_CTRL_SPD; /* * Build the DATA TDs */ plink = NULL; td = qh->dummy_td; do { /* Allow zero length packets */ int pktsze = maxsze; if (len <= pktsze) { /* The last packet */ pktsze = len; if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) status &= ~TD_CTRL_SPD; } if (plink) { td = uhci_alloc_td(uhci); if (!td) goto nomem; *plink = LINK_TO_TD(td); } uhci_add_td_to_urbp(td, urbp); uhci_fill_td(td, status, destination | uhci_explen(pktsze) | (toggle << TD_TOKEN_TOGGLE_SHIFT), data); plink = &td->link; status |= TD_CTRL_ACTIVE; data += pktsze; len -= maxsze; toggle ^= 1; } while (len > 0); /* * URB_ZERO_PACKET means adding a 0-length packet, if direction * is OUT and the transfer_length was an exact multiple of maxsze, * hence (len = transfer_length - N * maxsze) == 0 * however, if transfer_length == 0, the zero packet was already * prepared above. */ if ((urb->transfer_flags & URB_ZERO_PACKET) && usb_pipeout(urb->pipe) && len == 0 && urb->transfer_buffer_length > 0) { td = uhci_alloc_td(uhci); if (!td) goto nomem; *plink = LINK_TO_TD(td); uhci_add_td_to_urbp(td, urbp); uhci_fill_td(td, status, destination | uhci_explen(0) | (toggle << TD_TOKEN_TOGGLE_SHIFT), data); plink = &td->link; toggle ^= 1; } /* Set the interrupt-on-completion flag on the last packet. * A more-or-less typical 4 KB URB (= size of one memory page) * will require about 3 ms to transfer; that's a little on the * fast side but not enough to justify delaying an interrupt * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT * flag setting. */ td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); /* * Build the new dummy TD and activate the old one */ td = uhci_alloc_td(uhci); if (!td) goto nomem; *plink = LINK_TO_TD(td); uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); wmb(); qh->dummy_td->status |= __constant_cpu_to_le32(TD_CTRL_ACTIVE); qh->dummy_td = td; usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle); return 0;nomem: /* Remove the dummy TD from the td_list so it doesn't get freed */ uhci_remove_td_from_urbp(qh->dummy_td); return -ENOMEM;}static int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct uhci_qh *qh){ int ret; /* Can't have low-speed bulk transfers */ if (urb->dev->speed == USB_SPEED_LOW) return -EINVAL; if (qh->state != QH_STATE_ACTIVE) qh->skel = SKEL_BULK; ret = uhci_submit_common(uhci, urb, qh); if (ret == 0) uhci_add_fsbr(uhci, urb); return ret;}static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct uhci_qh *qh){ int ret; /* USB 1.1 interrupt transfers only involve one packet per interval. * Drivers can submit URBs of any length, but longer ones will need * multiple intervals to complete. */ if (!qh->bandwidth_reserved) { int exponent; /* Figure out which power-of-two queue to use */ for (exponent = 7; exponent >= 0; --exponent) { if ((1 << exponent) <= urb->interval) break; } if (exponent < 0) return -EINVAL; qh->period = 1 << exponent; qh->skel = SKEL_INDEX(exponent); /* For now, interrupt phase is fixed by the layout * of the QH lists. */ qh->phase = (qh->period / 2) & (MAX_PHASE - 1); ret = uhci_check_bandwidth(uhci, qh); if (ret) return ret; } else if (qh->period > urb->interval) return -EINVAL; /* Can't decrease the period */ ret = uhci_submit_common(uhci, urb, qh); if (ret == 0) { urb->interval = qh->period; if (!qh->bandwidth_reserved) uhci_reserve_bandwidth(uhci, qh); } return ret;}/* * Fix up the data structures following a short transfer */static int uhci_fixup_short_transfer(struct uhci_hcd *uhci, struct uhci_qh *qh, struct urb_priv *urbp){ struct uhci_td *td; struct list_head *tmp; int ret; td = list_entry(urbp->td_list.prev, struct uhci_td, list); if (qh->type == USB_ENDPOINT_XFER_CONTROL) { /* When a control transfer is short, we have to restart * the queue at the status stage transaction, which is * the last TD. */ WARN_ON(list_empty(&urbp->td_list)); qh->element = LINK_TO_TD(td); tmp = td->list.prev; ret = -EINPROGRESS; } else { /* When a bulk/interrupt transfer is short, we have to * fix up the toggles of the following URBs on the queue * before restarting the queue at the next URB. */ qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1; uhci_fixup_toggles(qh, 1); if (list_empty(&urbp->td_list)) td = qh->post_td; qh->element = td->link; tmp = urbp->td_list.prev; ret = 0; } /* Remove all the TDs we skipped over, from tmp back to the start */ while (tmp != &urbp->td_list) { td = list_entry(tmp, struct uhci_td, list); tmp = tmp->prev; uhci_remove_td_from_urbp(td); uhci_free_td(uhci, td); } return ret;}/* * Common result for control, bulk, and interrupt */static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb){ struct urb_priv *urbp = urb->hcpriv; struct uhci_qh *qh = urbp->qh; struct uhci_td *td, *tmp; unsigned status; int ret = 0; list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { unsigned int ctrlstat; int len; ctrlstat = td_status(td); status = uhci_status_bits(ctrlstat); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; len = uhci_actual_length(ctrlstat); urb->actual_length += len; if (status) { ret = uhci_map_status(status, uhci_packetout(td_token(td))); if ((debug == 1 && ret != -EPIPE) || debug > 1) { /* Some debugging code */ dev_dbg(&urb->dev->dev, "%s: failed with status %x\n", __FUNCTION__, status); if (debug > 1 && errbuf) { /* Print the chain for debugging */ uhci_show_qh(uhci, urbp->qh, errbuf, ERRBUF_LEN, 0); lprintk(errbuf); } } } else if (len < uhci_expected_length(td_token(td))) { /* We received a short packet */ if (urb->transfer_flags & URB_SHORT_NOT_OK) ret = -EREMOTEIO; /* Fixup needed only if this isn't the URB's last TD */ else if (&td->list != urbp->td_list.prev) ret = 1; } uhci_remove_td_from_urbp(td); if (qh->post_td) uhci_free_td(uhci, qh->post_td); qh->post_td = td; if (ret != 0) goto err; } return ret;err: if (ret < 0) { /* In case a control transfer gets an error * during the setup stage */ urb->actual_length = max(urb->actual_length, 0); /* Note that the queue has stopped and save * the next toggle value */ qh->element = UHCI_PTR_TERM; qh->is_stopped = 1; qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL); qh->initial_toggle = uhci_toggle(td_token(td)) ^ (ret == -EREMOTEIO); } else /* Short packet received */ ret = uhci_fixup_short_transfer(uhci, qh, urbp); return ret;}/* * Isochronous transfers */static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, struct uhci_qh *qh){ struct uhci_td *td = NULL; /* Since urb->number_of_packets > 0 */ int i, frame; unsigned long destination, status; struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv; /* Values must not be too big (could overflow below) */ if (urb->interval >= UHCI_NUMFRAMES || urb->number_of_packets >= UHCI_NUMFRAMES) return -EFBIG; /* Check the period and figure out the starting frame number */ if (!qh->bandwidth_reserved) { qh->period = urb->interval; if (urb->transfer_flags & URB_ISO_ASAP) { qh->phase = -1; /* Find the best phase */ i = uhci_check_bandwidth(uhci, qh); if (i) return i; /* Allow a little time to allocate the TDs */ uhci_get_current_frame_number(uhci); frame = uhci->frame_number + 10; /* Move forward to the first frame having the * correct phase */ urb->start_frame = frame + ((qh->phase - frame) & (qh->period - 1)); } else { i = urb->start_frame - uhci->last_iso_frame; if (i <= 0 || i >= UHCI_NUMFRAMES) return -EINVAL; qh->phase = urb->start_frame & (qh->period - 1); i = uhci_check_bandwidth(uhci, qh); if (i) return i; } } else if (qh->period != urb->interval) { return -EINVAL; /* Can't change the period */ } else { /* Pick up where the last URB leaves off */ if (list_empty(&qh->queue)) { frame = qh->iso_frame; } else { struct urb *lurb; lurb = list_entry(qh->queue.prev, struct urb_priv, node)->urb; frame = lurb->start_frame + lurb->number_of_packets * lurb->interval; } if (urb->transfer_flags & URB_ISO_ASAP) urb->start_frame = frame; else if (urb->start_frame != frame) return -EINVAL; } /* Make sure we won't have to go too far into the future */ if (uhci_frame_before_eq(uhci->last_iso_frame + UHCI_NUMFRAMES, urb->start_frame + urb->number_of_packets * urb->interval)) return -EFBIG; status = TD_CTRL_ACTIVE | TD_CTRL_IOS; destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); for (i = 0; i < urb->number_of_packets; i++) { td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; uhci_add_td_to_urbp(td, urbp); uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length), urb->transfer_dma + urb->iso_frame_desc[i].offset); } /* Set the interrupt-on-completion flag on the last packet. */ td->status |= __constant_cpu_to_le32(TD_CTRL_IOC); /* Add the TDs to the frame list */ frame = urb->start_frame; list_for_each_entry(td, &urbp->td_list, list) { uhci_insert_td_in_frame_list(uhci, td, frame); frame += qh->period; } if (list_empty(&qh->queue)) { qh->iso_packet_desc = &urb->iso_frame_desc[0]; qh->iso_frame = urb->start_frame;
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -