📄 uhci.c
字号:
err("unable to allocate new QH for control retrigger"); return -ENOMEM; } urbp->qh->urbp = urbp; /* One TD, who cares about Breadth first? */ uhci_insert_tds_in_qh(urbp->qh, urb, 0); /* Low speed or small transfers gets a different queue and treatment */ if (urb->pipe & TD_CTRL_LS) uhci_insert_qh(uhci, uhci->skel_ls_control_qh, urb); else uhci_insert_qh(uhci, uhci->skel_hs_control_qh, urb); return -EINPROGRESS;}/* * Interrupt transfers */static int uhci_submit_interrupt(struct urb *urb){ struct uhci_td *td; unsigned long destination, status; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) return -EINVAL; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC; td = uhci_alloc_td(uhci, urb->dev); if (!td) return -ENOMEM; destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT); destination |= ((urb->transfer_buffer_length - 1) << 21); usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination, urbp->transfer_buffer_dma_handle); uhci_insert_td(uhci, uhci->skeltd[__interval_to_skel(urb->interval)], td); return -EINPROGRESS;}static int uhci_result_interrupt(struct urb *urb){ struct list_head *tmp, *head; struct urb_priv *urbp = urb->hcpriv; struct uhci_td *td; unsigned int status; int ret = 0; urb->actual_length = 0; head = &urbp->td_list; tmp = head->next; while (tmp != 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; if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) { if (urb->transfer_flags & USB_DISABLE_SPD) { ret = -EREMOTEIO; goto err; } else return 0; } } 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_interrupt/bulk() failed with status %x", status); if (errbuf) { /* Print the chain for debugging purposes */ if (urbp->qh) uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0); else uhci_show_td(td, errbuf, ERRBUF_LEN, 0); lprintk(errbuf); } } return ret;}static void uhci_reset_interrupt(struct urb *urb){ struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci_td *td; unsigned long flags; spin_lock_irqsave(&urb->lock, flags); /* Root hub is special */ if (urb->dev == uhci->rh.dev) goto out; td = list_entry(urbp->td_list.next, struct uhci_td, list); td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; td->info &= ~TD_TOKEN_TOGGLE; td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE_SHIFT); usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));out: urb->status = -EINPROGRESS; spin_unlock_irqrestore(&urb->lock, flags);}/* * Bulk transfers */static int uhci_submit_bulk(struct urb *urb, struct urb *eurb){ struct uhci_td *td; struct uhci_qh *qh; unsigned long destination, status; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; 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 = urbp->transfer_buffer_dma_handle; if (len < 0 || maxsze <= 0) return -EINVAL; /* Can't have low speed bulk transfers */ if (urb->pipe & TD_CTRL_LS) return -EINVAL; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); /* 3 errors */ status = TD_CTRL_ACTIVE | (3 << TD_CTRL_C_ERR_SHIFT); if (!(urb->transfer_flags & USB_DISABLE_SPD)) status |= TD_CTRL_SPD; /* * Build the DATA TD's */ do { /* Allow zero length packets */ int pktsze = len; if (pktsze > maxsze) pktsze = maxsze; td = uhci_alloc_td(uhci, urb->dev); if (!td) return -ENOMEM; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | (((pktsze - 1) & UHCI_NULL_DATA_SIZE) << 21) | (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); /* * USB_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 & USB_ZERO_PACKET) && !len && urb->transfer_buffer_length) { 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_NULL_DATA_SIZE << 21) | (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 flag on the last packet */ td->status |= TD_CTRL_IOC; qh = uhci_alloc_qh(uhci, urb->dev); if (!qh) return -ENOMEM; urbp->qh = qh; qh->urbp = urbp; /* Always assume breadth first */ uhci_insert_tds_in_qh(qh, urb, 1); if (urb->transfer_flags & USB_QUEUE_BULK && eurb) uhci_append_queued_urb(uhci, eurb, urb); else uhci_insert_qh(uhci, uhci->skel_bulk_qh, urb); uhci_inc_fsbr(uhci, urb); return -EINPROGRESS;}/* We can use the result interrupt since they're identical */#define uhci_result_bulk uhci_result_interrupt/* * Isochronous transfers */static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigned int *end){ struct urb *last_urb = NULL; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; struct list_head *tmp, *head; int ret = 0; head = &uhci->urb_list; tmp = head->next; while (tmp != head) { struct urb *u = list_entry(tmp, struct urb, urb_list); tmp = tmp->next; /* 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) & 1023; ret = 0; } else ret = -1; /* no previous urb found */ return ret;}static int isochronous_find_start(struct urb *urb){ int limits; unsigned int start = 0, end = 0; if (urb->number_of_packets > 900) /* 900? Why? */ return -EFBIG; limits = isochronous_find_limits(urb, &start, &end); if (urb->transfer_flags & USB_ISO_ASAP) { if (limits) { int curframe; curframe = uhci_get_current_frame_number(urb->dev) % UHCI_NUMFRAMES; urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES; } else urb->start_frame = end; } else { urb->start_frame %= UHCI_NUMFRAMES; /* FIXME: Sanity check */ } return 0;}/* * Isochronous transfers */static int uhci_submit_isochronous(struct urb *urb){ struct uhci_td *td; struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; int i, ret, framenum; int status, destination; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; status = TD_CTRL_ACTIVE | TD_CTRL_IOS; destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); ret = isochronous_find_start(urb); if (ret) return ret; framenum = urb->start_frame; for (i = 0; i < urb->number_of_packets; i++, framenum++) { if (!urb->iso_frame_desc[i].length) continue; td = uhci_alloc_td(uhci, urb->dev); if (!td) return -ENOMEM; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21), urbp->transfer_buffer_dma_handle + urb->iso_frame_desc[i].offset); if (i + 1 >= urb->number_of_packets) td->status |= TD_CTRL_IOC; uhci_insert_td_frame_list(uhci, td, framenum); } return -EINPROGRESS;}static int uhci_result_isochronous(struct urb *urb){ struct list_head *tmp, *head; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; int status; int i, ret = 0; urb->actual_length = 0; i = 0; head = &urbp->td_list; tmp = head->next; while (tmp != head) { struct uhci_td *td = list_entry(tmp, struct uhci_td, list); int actlength; tmp = tmp->next; if (td->status & TD_CTRL_ACTIVE) return -EINPROGRESS; actlength = uhci_actual_length(td->status); urb->iso_frame_desc[i].actual_length = actlength; urb->actual_length += actlength; status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe)); urb->iso_frame_desc[i].status = status; if (status) { urb->error_count++; ret = status; } i++; } return ret;}/* * MUST be called with uhci->urb_list_lock acquired */static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb){ struct list_head *tmp, *head; /* We don't match Isoc transfers since they are special */ if (usb_pipeisoc(urb->pipe)) return NULL; head = &uhci->urb_list; tmp = head->next; while (tmp != head) { struct urb *u = list_entry(tmp, struct urb, urb_list); tmp = tmp->next; if (u->dev == urb->dev && u->pipe == urb->pipe && u->status == -EINPROGRESS) return u; } return NULL;}static int uhci_submit_urb(struct urb *urb){ int ret = -EINVAL; struct uhci *uhci; unsigned long flags; struct urb *eurb; int bustime; if (!urb) return -EINVAL; if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) { warn("uhci_submit_urb: urb %p belongs to disconnected device or bus?", urb); return -ENODEV; } uhci = (struct uhci *)urb->dev->bus->hcpriv; usb_inc_dev_use(urb->dev); spin_lock_irqsave(&uhci->urb_list_lock, flags); spin_lock(&urb->lock); if (urb->status == -EINPROGRESS || urb->status == -ECONNRESET || urb->status == -ECONNABORTED) { dbg("uhci_submit_urb: urb not available to submit (status = %d)", urb->status); /* Since we can have problems on the out path */ spin_unlock(&urb->lock); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); usb_dec_dev_use(urb->dev); return ret; } INIT_LIST_HEAD(&urb->urb_list); if (!uhci_alloc_urb_priv(uhci, urb)) { ret = -ENOMEM; goto out; } eurb = uhci_find_urb_ep(uhci, urb); if (eurb && !(urb->transfer_flags & USB_QUEUE_BULK)) { ret = -ENXIO; goto out; } /* Short circuit the virtual root hub */ if (urb->dev == uhci->rh.dev) { ret = rh_submit_urb(urb); goto out; } switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: ret = uhci_submit_control(urb); break; case PIPE_INTERRUPT: if (urb->bandwidth == 0) { /* not yet checked/allocated */ bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) ret = bustime; else { ret = uhci_submit_interrupt(urb); if (ret == -EINPROGRESS) usb_claim_bandwidth(urb->dev, urb, bustime, 0); } } else /* bandwidth is already set */ ret = uhci_submit_interrupt(urb); break; case PIPE_BULK: ret = uhci_submit_bulk(urb, eurb); break; case PIPE_ISOCHRONOUS: if (urb->bandwidth == 0) { /* not yet checked/allocated */ if (urb->number_of_packets <= 0) { ret = -EINVAL; break; } bustime = usb_check_bandwidth(urb->dev, urb); if (bustime < 0) { ret = bustime; break; } ret = uhci_submit_isochronous(urb); if (ret == -EINPROGRESS) usb_claim_bandwidth(urb->dev, urb, bustime, 1); } else /* bandwidth is already set */ ret = uhci_submit_isochronous(urb); break; }out: urb->status = ret; if (ret == -EINPROGRESS) { /* We use _tail to make find_urb_ep more efficient */ list_add_tail(&urb->urb_list, &uhci->urb_list); spin_unlock(&urb->lock); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); return 0; } uhci_unlink_generic(uhci, urb); spin_unlock(&urb->lock); spin_unlock_irqrestore(&uhci->urb_list_lock, flags); /* Only call completion if it was successful */ if (!ret) uhci_call_completion(urb);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -