📄 uhci-q.c
字号:
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); 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); 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); 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); 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); 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), data); data += pktsze; len -= maxsze; usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); } 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 (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) && !len && urb->transfer_buffer_length) { td = uhci_alloc_td(uhci); if (!td) return -ENOMEM; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(UHCI_NULL_DATA_SIZE) | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT), data); usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); } /* 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 |= cpu_to_le32(TD_CTRL_IOC); qh = uhci_alloc_qh(uhci); if (!qh) return -ENOMEM; urbp->qh = qh; qh->urbp = urbp; /* Always breadth first */ uhci_insert_tds_in_qh(qh, urb, UHCI_PTR_BREADTH); if (eurb) uhci_append_queued_urb(uhci, eurb, urb); else uhci_insert_qh(uhci, skelqh, urb); return -EINPROGRESS;}/* * 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; } else return 0; } } return 0;td_error: ret = uhci_map_status(status, uhci_packetout(td_token(td)));err: /* * Enable this chunk of code if you want to see some more debugging. * But be careful, it has the tendancy to starve out khubd and prevent * disconnects from happening successfully if you have a slow debug * log interface (like a serial console. */#if 0 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); } }#endif return ret;}static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb){ int ret; /* Can't have low-speed bulk transfers */ if (urb->dev->speed == USB_SPEED_LOW) return -EINVAL; ret = uhci_submit_common(uhci, urb, eurb, uhci->skel_bulk_qh); if (ret == -EINPROGRESS) uhci_inc_fsbr(uhci, urb); return ret;}static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, struct urb *eurb){ /* USB 1.1 interrupt transfers only involve one packet per interval; * that's the uhci_submit_common() "breadth first" policy. Drivers * can submit urbs of any length, but longer ones might need many * intervals to complete. */ return uhci_submit_common(uhci, urb, eurb, uhci->skelqh[__interval_to_skel(urb->interval)]);}/* * Isochronous transfers */static int isochronous_find_limits(struct uhci_hcd *uhci, struct urb *urb, unsigned int *start, unsigned int *end){ struct urb *last_urb = NULL; struct urb_priv *up; int ret = 0; list_for_each_entry(up, &uhci->urb_list, urb_list) { struct urb *u = up->urb; /* look for pending URB's with identical pipe handle */ if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS) && (u != urb)) { if (!last_urb) *start = u->start_frame; last_urb = u; } } if (last_urb) { *end = (last_urb->start_frame + last_urb->number_of_packets * last_urb->interval) & (UHCI_NUMFRAMES-1); ret = 0; } else ret = -1; /* no previous urb found */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -