func: static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_t *int_mask)
  
  {
  
      int len = 0, max_len, err, ret;
  
      uint8_t pid;
  
  
  
      max_len = ((td->token >> 21) + 1) & 0x7ff;
  
      pid = td->token & 0xff;
  
  
  
      ret = async->packet.result;
  
  
  
      if (td->ctrl & TD_CTRL_IOS)
  
          td->ctrl &= ~TD_CTRL_ACTIVE;
  
  
  
      if (ret < 0)
  
          goto out;
  
  
  
      len = async->packet.result;
  
      td->ctrl = (td->ctrl & ~0x7ff) | ((len - 1) & 0x7ff);
  
  
  
      /* The NAK bit may have been set by a previous frame, so clear it
  
         here.  The docs are somewhat unclear, but win2k relies on this
  
         behavior.  */
  
      td->ctrl &= ~(TD_CTRL_ACTIVE | TD_CTRL_NAK);
  
      if (td->ctrl & TD_CTRL_IOC)
  
          *int_mask |= 0x01;
  
  
  
      if (pid == USB_TOKEN_IN) {
  
          if (len > max_len) {
  
              ret = USB_RET_BABBLE;
  
              goto out;
  
          }
  
  
  
          if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
  
              *int_mask |= 0x02;
  
              /* short packet: do not update QH */
  
              trace_usb_uhci_packet_complete_shortxfer(async->queue->token,
  
                                                      async->td);
  
              return TD_RESULT_NEXT_QH;
  
          }
  
      }
  
  
  
      /* success */
  
      trace_usb_uhci_packet_complete_success(async->queue->token, async->td);
  
      return TD_RESULT_COMPLETE;
  
  
  
  out:
  
      /*
  
       * We should not do any further processing on a queue with errors!
  
       * This is esp. important for bulk endpoints with pipelining enabled
  
       * (redirection to a real USB device), where we must cancel all the
  
       * transfers after this one so that:
  
       * 1) If they've completed already, they are not processed further
  
       *    causing more stalls, originating from the same failed transfer
  
       * 2) If still in flight, they are cancelled before the guest does
  
       *    a clear stall, otherwise the guest and device can loose sync!
  
       */
  
      while (!QTAILQ_EMPTY(&async->queue->asyncs)) {
  
          UHCIAsync *as = QTAILQ_FIRST(&async->queue->asyncs);
  
          uhci_async_unlink(as);
  
          uhci_async_cancel(as);
  
      }
  
  
  
      switch(ret) {
  
      case USB_RET_STALL:
  
          td->ctrl |= TD_CTRL_STALL;
  
          td->ctrl &= ~TD_CTRL_ACTIVE;
  
          s->status |= UHCI_STS_USBERR;
  
          if (td->ctrl & TD_CTRL_IOC) {
  
              *int_mask |= 0x01;
  
          }
  
          uhci_update_irq(s);
  
          trace_usb_uhci_packet_complete_stall(async->queue->token, async->td);
  
          return TD_RESULT_NEXT_QH;
  
  
  
      case USB_RET_BABBLE:
  
          td->ctrl |= TD_CTRL_BABBLE | TD_CTRL_STALL;
  
          td->ctrl &= ~TD_CTRL_ACTIVE;
  
          s->status |= UHCI_STS_USBERR;
  
          if (td->ctrl & TD_CTRL_IOC) {
  
              *int_mask |= 0x01;
  
          }
  
          uhci_update_irq(s);
  
          /* frame interrupted */
  
          trace_usb_uhci_packet_complete_babble(async->queue->token, async->td);
  
          return TD_RESULT_STOP_FRAME;
  
  
  
      case USB_RET_NAK:
  
          td->ctrl |= TD_CTRL_NAK;
  
          if (pid == USB_TOKEN_SETUP)
  
              break;
  
          return TD_RESULT_NEXT_QH;
  
  
  
      case USB_RET_IOERROR:
  
      case USB_RET_NODEV:
  
      default:
  
  	break;
  
      }
  
  
  
      /* Retry the TD if error count is not zero */
  
  
  
      td->ctrl |= TD_CTRL_TIMEOUT;
  
      err = (td->ctrl >> TD_CTRL_ERROR_SHIFT) & 3;
  
      if (err != 0) {
  
          err--;
  
          if (err == 0) {
  
              td->ctrl &= ~TD_CTRL_ACTIVE;
  
              s->status |= UHCI_STS_USBERR;
  
              if (td->ctrl & TD_CTRL_IOC)
  
                  *int_mask |= 0x01;
  
              uhci_update_irq(s);
  
              trace_usb_uhci_packet_complete_error(async->queue->token,
  
                                                   async->td);
  
          }
  
      }
  
      td->ctrl = (td->ctrl & ~(3 << TD_CTRL_ERROR_SHIFT)) |
  
          (err << TD_CTRL_ERROR_SHIFT);
  
      return TD_RESULT_NEXT_QH;
  
  }
  