📄 uhci.c
字号:
furbp = list_entry(tmp, struct urb_priv, queue_list); lurbp = list_entry(furbp->queue_list.prev, struct urb_priv, queue_list); lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list); usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1)); /* All qh's in the queue need to link to the next queue */ urbp->qh->link = eurbp->qh->link; mb(); /* Make sure we flush everything */ /* Only support bulk right now, so no depth */ lltd->link = urbp->qh->dma_handle | UHCI_PTR_QH; list_add_tail(&urbp->queue_list, &furbp->queue_list); urbp->queued = 1; spin_unlock_irqrestore(&uhci->frame_list_lock, flags);}static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb){ struct urb_priv *urbp, *nurbp; struct list_head *head, *tmp; struct urb_priv *purbp; struct uhci_td *pltd; unsigned int toggle; unsigned long flags; urbp = urb->hcpriv; spin_lock_irqsave(&uhci->frame_list_lock, flags); if (list_empty(&urbp->queue_list)) goto out; nurbp = list_entry(urbp->queue_list.next, struct urb_priv, queue_list); /* Fix up the toggle for the next URB's */ if (!urbp->queued) /* We set the toggle when we unlink */ 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(pltd->info) ^ 1; } head = &urbp->queue_list; tmp = head->next; while (head != tmp) { struct urb_priv *turbp; turbp = list_entry(tmp, struct urb_priv, queue_list); tmp = tmp->next; 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) { nurbp->queued = 0; _uhci_insert_qh(uhci, uhci->skel_bulk_qh, nurbp->urb); } else { /* We're somewhere in the middle (or end). A bit trickier */ /* than the head scenario */ 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 = 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; } list_del_init(&urbp->queue_list);out: spin_unlock_irqrestore(&uhci->frame_list_lock, flags);}static struct urb_priv *uhci_alloc_urb_priv(struct uhci *uhci, struct urb *urb){ struct urb_priv *urbp; urbp = kmem_cache_alloc(uhci_up_cachep, SLAB_ATOMIC); if (!urbp) { err("uhci_alloc_urb_priv: couldn't allocate memory for urb_priv\n"); return NULL; } memset((void *)urbp, 0, sizeof(*urbp)); urbp->inserttime = jiffies; urbp->fsbrtime = jiffies; urbp->urb = urb; urbp->dev = urb->dev; INIT_LIST_HEAD(&urbp->td_list); INIT_LIST_HEAD(&urbp->queue_list); INIT_LIST_HEAD(&urbp->complete_list); urb->hcpriv = urbp; if (urb->dev != uhci->rh.dev) { if (urb->transfer_buffer_length) { urbp->transfer_buffer_dma_handle = pci_map_single(uhci->dev, urb->transfer_buffer, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); if (!urbp->transfer_buffer_dma_handle) return NULL; } if (usb_pipetype(urb->pipe) == PIPE_CONTROL && urb->setup_packet) { urbp->setup_packet_dma_handle = pci_map_single(uhci->dev, urb->setup_packet, sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); if (!urbp->setup_packet_dma_handle) return NULL; } } return urbp;}/* * MUST be called with urb->lock acquired */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);}/* * MUST be called with urb->lock acquired */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;}/* * MUST be called with urb->lock acquired */static void uhci_destroy_urb_priv(struct urb *urb){ struct list_head *head, *tmp; struct urb_priv *urbp; struct uhci *uhci; urbp = (struct urb_priv *)urb->hcpriv; if (!urbp) return; if (!urbp->dev || !urbp->dev->bus || !urbp->dev->bus->hcpriv) { warn("uhci_destroy_urb_priv: urb %p belongs to disconnected device or bus?", urb); return; } if (!list_empty(&urb->urb_list)) warn("uhci_destroy_urb_priv: urb %p still on uhci->urb_list or uhci->remove_list", urb); if (!list_empty(&urbp->complete_list)) warn("uhci_destroy_urb_priv: urb %p still on uhci->complete_list", urb); uhci = urbp->dev->bus->hcpriv; head = &urbp->td_list; tmp = head->next; while (tmp != head) { struct uhci_td *td = list_entry(tmp, struct uhci_td, list); tmp = tmp->next; uhci_remove_td_from_urb(td); uhci_remove_td(uhci, td); uhci_free_td(uhci, td); } if (urbp->setup_packet_dma_handle) { pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle, sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); urbp->setup_packet_dma_handle = 0; } if (urbp->transfer_buffer_dma_handle) { pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle, urb->transfer_buffer_length, usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); urbp->transfer_buffer_dma_handle = 0; } urb->hcpriv = NULL; kmem_cache_free(uhci_up_cachep, urbp);}static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb){ unsigned long flags; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; spin_lock_irqsave(&uhci->frame_list_lock, flags); if ((!(urb->transfer_flags & USB_NO_FSBR)) && !urbp->fsbr) { urbp->fsbr = 1; if (!uhci->fsbr++ && !uhci->fsbrtimeout) uhci->skel_term_qh->link = uhci->skel_hs_control_qh->dma_handle | UHCI_PTR_QH; } spin_unlock_irqrestore(&uhci->frame_list_lock, flags);}static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb){ unsigned long flags; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; spin_lock_irqsave(&uhci->frame_list_lock, flags); if ((!(urb->transfer_flags & USB_NO_FSBR)) && urbp->fsbr) { urbp->fsbr = 0; if (!--uhci->fsbr) uhci->fsbrtimeout = jiffies + FSBR_DELAY; } spin_unlock_irqrestore(&uhci->frame_list_lock, flags);}/* * Map status to standard result codes * * <status> is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)] * <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 -ETIMEDOUT; else return -EILSEQ; } if (status & TD_CTRL_NAK) /* NAK */ return -ETIMEDOUT; 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; if (status & TD_CTRL_ACTIVE) /* Active */ return 0; return -EINVAL;}/* * Control transfers */static int uhci_submit_control(struct urb *urb){ struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; 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; dma_addr_t data = urbp->transfer_buffer_dma_handle; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; /* 3 errors */ status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); /* * Build the TD for the control request */ td = uhci_alloc_td(uhci, urb->dev); if (!td) return -ENOMEM; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | (7 << 21), urbp->setup_packet_dma_handle); /* * If direction is "send", change the frame from SETUP (0x2D) * to OUT (0xE1). Else change it from SETUP to IN (0x69). */ destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe)); if (!(urb->transfer_flags & USB_DISABLE_SPD)) 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 | ((pktsze - 1) << 21), 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_NULL_DATA_SIZE << 21), 0); qh = uhci_alloc_qh(uhci, urb->dev); if (!qh) return -ENOMEM; urbp->qh = qh; qh->urbp = urbp; /* Low speed or small transfers gets a different queue and treatment */ if (urb->pipe & TD_CTRL_LS) { uhci_insert_tds_in_qh(qh, urb, 0); uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb); } else { uhci_insert_tds_in_qh(qh, urb, 1); uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb); uhci_inc_fsbr(uhci, urb); } return -EINPROGRESS;}static int usb_control_retrigger_status(struct urb *urb);static int uhci_result_control(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_phase; } tmp = head->next; td = list_entry(tmp, struct uhci_td, list); /* The first TD is the SETUP phase, check the status, but skip */ /* the count */ status = uhci_status_bits(td->status); 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) { td = list_entry(tmp, struct uhci_td, list); tmp = tmp->next; status = uhci_status_bits(td->status); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; urb->actual_length += uhci_actual_length(td->status); if (status) goto td_error; /* Check to see if we received a short packet */ if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) { if (urb->transfer_flags & USB_DISABLE_SPD) { ret = -EREMOTEIO; goto err; } if (uhci_packetid(td->info) == USB_PID_IN) return usb_control_retrigger_status(urb); else return 0; } }status_phase: td = list_entry(tmp, struct uhci_td, list); /* Control status phase */ status = uhci_status_bits(td->status);#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 (td->status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */ status & TD_CTRL_ACTIVE && status & TD_CTRL_NAK) return 0;#endif if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; if (status) goto td_error; return 0;td_error: ret = uhci_map_status(status, uhci_packetout(td->info)); if (ret == -EPIPE) /* endpoint has stalled - mark it halted */ usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), uhci_packetout(td->info));err: if ((debug == 1 && ret != -EPIPE) || debug > 1) { /* Some debugging code */ dbg("uhci_result_control() failed with status %x", status); if (errbuf) { /* Print the chain for debugging purposes */ uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); lprintk(errbuf); } } return ret;}static int usb_control_retrigger_status(struct urb *urb){ struct list_head *tmp, *head; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci *uhci = urb->dev->bus->hcpriv; urbp->short_control_packet = 1; /* Create a new QH to avoid pointer overwriting problems */ uhci_remove_qh(uhci, urbp->qh); /* Delete all of the TD's except for the status TD at the end */ head = &urbp->td_list; tmp = head->next; while (tmp != head && tmp->next != head) { struct uhci_td *td = list_entry(tmp, struct uhci_td, list); tmp = tmp->next; uhci_remove_td_from_urb(td); uhci_remove_td(uhci, td); uhci_free_td(uhci, td); } urbp->qh = uhci_alloc_qh(uhci, urb->dev); if (!urbp->qh) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -