📄 uhci-q.c
字号:
static int uhci_map_status(int status, int dir_out){ if (!status) return 0; if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ return -EPROTO; if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ if (dir_out) return -EPROTO; else return -EILSEQ; } if (status & TD_CTRL_BABBLE) /* Babble */ return -EOVERFLOW; if (status & TD_CTRL_DBUFERR) /* Buffer error */ return -ENOSR; if (status & TD_CTRL_STALLED) /* Stalled */ return -EPIPE; WARN_ON(status & TD_CTRL_ACTIVE); /* Active */ return 0;}/* * Control transfers */static int uhci_submit_control(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; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; /* 3 errors, dummy TD remains inactive */ status = uhci_maxerr(3); if (urb->dev->speed == USB_SPEED_LOW) status |= TD_CTRL_LS; /* * Build the TD for the control request setup packet */ td = qh->dummy_td; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(8), urb->setup_dma); plink = &td->link; status |= TD_CTRL_ACTIVE; /* * If direction is "send", change the packet ID from SETUP (0x2D) * to OUT (0xE1). Else change it from SETUP to IN (0x69) and * set Short Packet Detect (SPD) for all data packets. */ if (usb_pipeout(urb->pipe)) destination ^= (USB_PID_SETUP ^ USB_PID_OUT); else { destination ^= (USB_PID_SETUP ^ USB_PID_IN); status |= TD_CTRL_SPD; } /* * Build the DATA TDs */ while (len > 0) { int pktsze = min(len, maxsze); td = uhci_alloc_td(uhci); if (!td) goto nomem; *plink = cpu_to_le32(td->dma_handle); /* Alternate Data0/1 (start with Data1) */ destination ^= TD_TOKEN_TOGGLE; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(pktsze), data); plink = &td->link; data += pktsze; len -= pktsze; } /* * Build the final TD for control status */ td = uhci_alloc_td(uhci); if (!td) goto nomem; *plink = cpu_to_le32(td->dma_handle); /* * It's IN if the pipe is an output pipe or we're not expecting * data back. */ destination &= ~TD_TOKEN_PID_MASK; if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) destination |= USB_PID_IN; else destination |= USB_PID_OUT; destination |= TD_TOKEN_TOGGLE; /* End in Data1 */ status &= ~TD_CTRL_SPD; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status | TD_CTRL_IOC, destination | uhci_explen(0), 0); plink = &td->link; /* * Build the new dummy TD and activate the old one */ td = uhci_alloc_td(uhci); if (!td) goto nomem; *plink = cpu_to_le32(td->dma_handle); 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) qh->skel = uhci->skel_ls_control_qh; else { qh->skel = uhci->skel_fs_control_qh; uhci_inc_fsbr(uhci, urb); } return 0;nomem: /* Remove the dummy TD from the td_list so it doesn't get freed */ uhci_remove_td_from_urb(qh->dummy_td); return -ENOMEM;}/* * If control-IN transfer was short, the status packet wasn't sent. * This routine changes the element pointer in the QH to point at the * status TD. It's safe to do this even while the QH is live, because * the hardware only updates the element pointer following a successful * transfer. The inactive TD for the short packet won't cause an update, * so the pointer won't get overwritten. The next time the controller * sees this QH, it will send the status packet. */static int usb_control_retrigger_status(struct uhci_hcd *uhci, struct urb *urb){ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci_td *td; urbp->short_transfer = 1; td = list_entry(urbp->td_list.prev, struct uhci_td, list); urbp->qh->element = cpu_to_le32(td->dma_handle); return -EINPROGRESS;}static int uhci_result_control(struct uhci_hcd *uhci, struct urb *urb){ struct list_head *tmp, *head; struct urb_priv *urbp = urb->hcpriv; struct uhci_td *td; unsigned int status; int ret = 0; head = &urbp->td_list; if (urbp->short_transfer) { tmp = head->prev; goto status_stage; } urb->actual_length = 0; tmp = head->next; td = list_entry(tmp, struct uhci_td, list); /* The first TD is the SETUP stage, check the status, but skip */ /* the count */ status = uhci_status_bits(td_status(td)); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; if (status) goto td_error; /* The rest of the TDs (but the last) are data */ tmp = tmp->next; while (tmp != head && tmp->next != head) { unsigned int ctrlstat; td = list_entry(tmp, struct uhci_td, list); tmp = tmp->next; ctrlstat = td_status(td); status = uhci_status_bits(ctrlstat); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; urb->actual_length += uhci_actual_length(ctrlstat); if (status) goto td_error; /* Check to see if we received a short packet */ if (uhci_actual_length(ctrlstat) < uhci_expected_length(td_token(td))) { if (urb->transfer_flags & URB_SHORT_NOT_OK) { ret = -EREMOTEIO; goto err; } return usb_control_retrigger_status(uhci, urb); } }status_stage: td = list_entry(tmp, struct uhci_td, list); /* Control status stage */ status = td_status(td);#ifdef I_HAVE_BUGGY_APC_BACKUPS /* APC BackUPS Pro kludge */ /* It tries to send all of the descriptor instead of the amount */ /* we requested */ if (status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */ status & TD_CTRL_ACTIVE && status & TD_CTRL_NAK) return 0;#endif status = uhci_status_bits(status); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; if (status) goto td_error; return 0;td_error: ret = uhci_map_status(status, uhci_packetout(td_token(td)));err: if ((debug == 1 && ret != -EPIPE) || debug > 1) { /* Some debugging code */ dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n", __FUNCTION__, status); if (errbuf) { /* Print the chain for debugging purposes */ uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); lprintk(errbuf); } } /* Note that the queue has stopped */ urbp->qh->element = UHCI_PTR_TERM; urbp->qh->is_stopped = 1; return ret;}/* * 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; 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 = cpu_to_le32(td->dma_handle); } uhci_add_td_to_urb(urb, td); 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 = cpu_to_le32(td->dma_handle); uhci_add_td_to_urb(urb, td); 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 = cpu_to_le32(td->dma_handle); 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_urb(qh->dummy_td); return -ENOMEM;}/* * Common result for bulk and interrupt */static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb){ struct urb_priv *urbp = urb->hcpriv; struct uhci_td *td; unsigned int status = 0; int ret = 0; urb->actual_length = 0; list_for_each_entry(td, &urbp->td_list, list) { unsigned int ctrlstat = td_status(td); status = uhci_status_bits(ctrlstat); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; urb->actual_length += uhci_actual_length(ctrlstat); if (status) goto td_error; if (uhci_actual_length(ctrlstat) < uhci_expected_length(td_token(td))) { if (urb->transfer_flags & URB_SHORT_NOT_OK) { ret = -EREMOTEIO; goto err; } /* * This URB stopped short of its end. We have to * fix up the toggles of the following URBs on the * queue and restart the queue. But only if this * TD isn't the last one in the URB. * * Do this only the first time we encounter the * short URB. */ if (!urbp->short_transfer && &td->list != urbp->td_list.prev) { urbp->short_transfer = 1; urbp->qh->initial_toggle = uhci_toggle(td_token(td)) ^ 1; uhci_fixup_toggles(urbp->qh, 1); td = list_entry(urbp->td_list.prev, struct uhci_td, list); urbp->qh->element = td->link; } break; } } return 0;td_error: ret = uhci_map_status(status, uhci_packetout(td_token(td))); if ((debug == 1 && ret != -EPIPE) || debug > 1) { /* Some debugging code */ dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n", __FUNCTION__, status); if (debug > 1 && errbuf) { /* Print the chain for debugging purposes */ uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); lprintk(errbuf); } }err: /* Note that the queue has stopped and save the next toggle value */ urbp->qh->element = UHCI_PTR_TERM; urbp->qh->is_stopped = 1; urbp->qh->needs_fixup = 1; urbp->qh->initial_toggle = uhci_toggle(td_token(td)) ^
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -