+/*
+ * 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_transfer = 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;
+
+ head = &urbp->td_list;
+ if (urbp->short_transfer) {
+ tmp = head->prev;
+ goto status_stage;
+ }
+
+ urb->actual_length = 0;
+
+ 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;
+
+ /* The rest of the TDs (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;
+ }
+
+ return usb_control_retrigger_status(uhci, urb);
+ }
+ }
+
+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);
+ }
+ }
+
+ /* Note that the queue has stopped */
+ urbp->qh->element = UHCI_PTR_TERM;
+ urbp->qh->is_stopped = 1;
+ return ret;
+}
+