📄 uhci-hcd.c
字号:
nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list); /* * Fix up the toggle for the following URBs in the queue. * Only needed for bulk and interrupt: control and isochronous * endpoints don't propagate toggles between messages. */ if (usb_pipebulk(urb->pipe) || usb_pipeint(urb->pipe)) { if (!urbp->queued) /* We just set the toggle in uhci_unlink_generic */ toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); else { /* If we're in the middle of the queue, grab the */ /* toggle from the TD previous to us */ purbp = list_entry(urbp->queue_list.prev, struct urb_priv, queue_list); pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); toggle = uhci_toggle(td_token(pltd)) ^ 1; } list_for_each_entry(turbp, &urbp->queue_list, queue_list) { if (!turbp->queued) break; toggle = uhci_fixup_toggle(turbp->urb, toggle); } usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), toggle); } if (urbp->queued) { /* We're somewhere in the middle (or end). The case where * we're at the head is handled in uhci_remove_qh(). */ purbp = list_entry(urbp->queue_list.prev, struct urb_priv, queue_list); pltd = list_entry(purbp->td_list.prev, struct uhci_td, list); if (nurbp->queued) pltd->link = cpu_to_le32(nurbp->qh->dma_handle) | UHCI_PTR_QH; else /* The next URB happens to be the beginning, so */ /* we're the last, end the chain */ pltd->link = UHCI_PTR_TERM; } /* urbp->queue_list is handled in uhci_remove_qh() */}static struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, struct urb *urb){ struct urb_priv *urbp; urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC); if (!urbp) return NULL; memset((void *)urbp, 0, sizeof(*urbp)); urbp->inserttime = jiffies; urbp->fsbrtime = jiffies; urbp->urb = urb; INIT_LIST_HEAD(&urbp->td_list); INIT_LIST_HEAD(&urbp->queue_list); INIT_LIST_HEAD(&urbp->urb_list); list_add_tail(&urbp->urb_list, &uhci->urb_list); urb->hcpriv = urbp; return urbp;}static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td){ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; td->urb = urb; list_add_tail(&td->list, &urbp->td_list);}static void uhci_remove_td_from_urb(struct uhci_td *td){ if (list_empty(&td->list)) return; list_del_init(&td->list); td->urb = NULL;}static void uhci_destroy_urb_priv(struct uhci_hcd *uhci, struct urb *urb){ struct uhci_td *td, *tmp; struct urb_priv *urbp; unsigned int age; urbp = (struct urb_priv *)urb->hcpriv; if (!urbp) return; if (!list_empty(&urbp->urb_list)) dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list " "or uhci->remove_list!\n", urb); age = uhci_get_current_frame_number(uhci); if (age != uhci->td_remove_age) { uhci_free_pending_tds(uhci); uhci->td_remove_age = age; } /* Check to see if the remove list is empty. Set the IOC bit */ /* to force an interrupt so we can remove the TD's*/ if (list_empty(&uhci->td_remove_list)) uhci_set_next_interrupt(uhci); list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { uhci_remove_td_from_urb(td); uhci_remove_td(uhci, td); list_add(&td->remove_list, &uhci->td_remove_list); } urb->hcpriv = NULL; kmem_cache_free(uhci_up_cachep, urbp);}static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb){ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) { urbp->fsbr = 1; if (!uhci->fsbr++ && !uhci->fsbrtimeout) uhci->skel_term_qh->link = cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH; }}static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb){ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) { urbp->fsbr = 0; if (!--uhci->fsbr) uhci->fsbrtimeout = jiffies + FSBR_DELAY; }}/* * Map status to standard result codes * * <status> is (td_status(td) & 0xF60000), a.k.a. * uhci_status_bits(td_status(td)). * Note: <status> does not include the TD_CTRL_NAK bit. * <dir_out> is True for output TDs and False for input TDs. */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 urb *eurb){ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci_td *td; struct uhci_qh *qh, *skelqh; unsigned long destination, status; int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; /* 3 errors */ status = TD_CTRL_ACTIVE | uhci_maxerr(3); if (urb->dev->speed == USB_SPEED_LOW) status |= TD_CTRL_LS; /* * Build the TD for the control request setup packet */ td = uhci_alloc_td(uhci, urb->dev); if (!td) return -ENOMEM; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(7), urb->setup_dma); /* * 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 TD's */ while (len > 0) { int pktsze = len; if (pktsze > maxsze) pktsze = maxsze; td = uhci_alloc_td(uhci, urb->dev); if (!td) return -ENOMEM; /* 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 - 1), data); data += pktsze; len -= pktsze; } /* * Build the final TD for control status */ td = uhci_alloc_td(uhci, urb->dev); if (!td) return -ENOMEM; /* * 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(UHCI_NULL_DATA_SIZE), 0); qh = uhci_alloc_qh(uhci, urb->dev); if (!qh) return -ENOMEM; urbp->qh = qh; qh->urbp = urbp; uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); /* 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 * is in the DEFAULT state. */ if (urb->dev->speed == USB_SPEED_LOW || urb->dev->state == USB_STATE_DEFAULT) skelqh = uhci->skel_ls_control_qh; else { skelqh = uhci->skel_fs_control_qh; uhci_inc_fsbr(uhci, urb); } if (eurb) uhci_append_queued_urb(uhci, eurb, urb); else uhci_insert_qh(uhci, skelqh, urb); return -EINPROGRESS;}/* * 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_control_packet = 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; if (list_empty(&urbp->td_list)) return -EINVAL; head = &urbp->td_list; if (urbp->short_control_packet) { tmp = head->prev; goto status_stage; } 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; urb->actual_length = 0; /* The rest of the TD's (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; } if (uhci_packetid(td_token(td)) == USB_PID_IN) return usb_control_retrigger_status(uhci, urb); else return 0; } }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); } } return ret;}/* * Common submit for bulk and interrupt */static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb, struct uhci_qh *skelqh){ struct uhci_td *td; struct uhci_qh *qh; unsigned long destination, status; int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); int len = urb->transfer_buffer_length; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; dma_addr_t data = urb->transfer_dma; 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); status = uhci_maxerr(3) | TD_CTRL_ACTIVE; if (urb->dev->speed == USB_SPEED_LOW) status |= TD_CTRL_LS; if (usb_pipein(urb->pipe)) status |= TD_CTRL_SPD; /* * Build the DATA TD's */ do { /* Allow zero length packets */ int pktsze = maxsze; if (pktsze >= len) { pktsze = len; if (!(urb->transfer_flags & URB_SHORT_NOT_OK)) status &= ~TD_CTRL_SPD; } td = uhci_alloc_td(uhci, urb->dev); if (!td) return -ENOMEM; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(pktsze - 1) | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -