📄 uhci.c
字号:
{ 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; unsigned char *data = urb->transfer_buffer; /* 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(urb->dev); if (!td) return -ENOMEM; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | (7 << 21), virt_to_bus(urb->setup_packet)); /* * 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(urb->dev); if (!td) return -ENOMEM; /* Alternate Data0/1 (start with Data1) */ destination ^= 1 << TD_TOKEN_TOGGLE; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | ((pktsze - 1) << 21), virt_to_bus(data)); data += pktsze; len -= pktsze; } /* * Build the final TD for control status */ td = uhci_alloc_td(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_PID; if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) destination |= USB_PID_IN; else destination |= USB_PID_OUT; destination |= 1 << 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(urb->dev); if (!qh) return -ENOMEM; /* 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, qh); } else { uhci_insert_tds_in_qh(qh, urb, 1); uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, qh); uhci_inc_fsbr(uhci, urb); } urbp->qh = qh; uhci_add_urb_list(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 (!urbp) return -EINVAL; head = &urbp->list; if (head->next == head) return -EINVAL; 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; if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) && !(td->status & TD_CTRL_ACTIVE)) { uhci_inc_fsbr(urb->dev->bus->hcpriv, urb); urbp->fsbr_timeout = 0; td->status &= ~TD_CTRL_IOC; } 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 && ret != -EPIPE) { /* Some debugging code */ dbg("uhci_result_control() failed with status %x", status); /* Print the chain for debugging purposes */ uhci_show_urb_queue(urb); } 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->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(urb, td); uhci_remove_td(uhci, td); uhci_free_td(td); } urbp->qh = uhci_alloc_qh(urb->dev); if (!urbp->qh) { err("unable to allocate new QH for control retrigger"); return -ENOMEM; } /* 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, urbp->qh); else uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh); 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; 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(urb->dev); if (!td) return -ENOMEM; destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE); 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, virt_to_bus(urb->transfer_buffer)); uhci_insert_td(uhci, &uhci->skeltd[__interval_to_skel(urb->interval)], td); uhci_add_urb_list(uhci, urb); 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; if (!urbp) return -EINVAL; urb->actual_length = 0; head = &urbp->list; tmp = head->next; while (tmp != head) { td = list_entry(tmp, struct uhci_td, list); tmp = tmp->next; if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) && !(td->status & TD_CTRL_ACTIVE)) { uhci_inc_fsbr(urb->dev->bus->hcpriv, urb); urbp->fsbr_timeout = 0; td->status &= ~TD_CTRL_IOC; } 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)) { usb_settoggle(urb->dev, uhci_endpoint(td->info), uhci_packetout(td->info), uhci_toggle(td->info) ^ 1); 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 && ret != -EPIPE) { /* Some debugging code */ dbg("uhci_result_interrupt/bulk() failed with status %x", status); /* Print the chain for debugging purposes */ if (urbp->qh) uhci_show_urb_queue(urb); else uhci_show_td(td); } return ret;}static void uhci_reset_interrupt(struct urb *urb){ struct list_head *tmp; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; struct uhci_td *td; if (!urbp) return; tmp = urbp->list.next; td = list_entry(tmp, struct uhci_td, list); if (!td) return; td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; td->info &= ~(1 << TD_TOKEN_TOGGLE); td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE); usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); urb->status = -EINPROGRESS;}/* * 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; unsigned char *data = urb->transfer_buffer; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; if (len < 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(urb->dev); if (!td) return -ENOMEM; uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) | (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE), virt_to_bus(data)); data += pktsze; len -= maxsze; if (len <= 0) td->status |= TD_CTRL_IOC; usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); } while (len > 0); qh = uhci_alloc_qh(urb->dev); if (!qh) return -ENOMEM; urbp->qh = qh; /* Always assume depth first */ uhci_insert_tds_in_qh(qh, urb, 1); if (urb->transfer_flags & USB_QUEUE_BULK && eurb) { urbp->queued = 1; uhci_append_queued_urb(uhci, eurb, urb); } else uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh); uhci_add_urb_list(uhci, 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 = &uhci->urb_list; int ret = 0; unsigned long flags; nested_lock(&uhci->urblist_lock, flags); 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 */ nested_unlock(&uhci->urblist_lock, flags); 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;}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; 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(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), virt_to_bus(urb->transfer_buffer + 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); } uhci_add_urb_list(uhci, urb); 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; if (!urbp) return -EINVAL; urb->actual_length = 0; i = 0; head = &urbp->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 != 0) { urb->error_count++; ret = status; } i++; } return ret;}static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb){ struct list_head *tmp, *head = &uhci->urb_list; unsigned long flags; struct urb *u = NULL; if (usb_pipeisoc(urb->pipe)) return NULL; nested_lock(&uhci->urblist_lock, flags);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -