mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-06 06:37:59 +00:00
[PATCH] UHCI: Remove non-iso TDs as they are used
This patch (as680) frees non-isochronous TDs as they are used, rather than all at once when an URB is complete. Although not a terribly important change in itself, it opens the door to a later enhancement that will reduce storage requirements by allocating only a limited number of TDs at any time for each endpoint queue. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
b1869000a6
commit
59e29ed91c
3 changed files with 45 additions and 39 deletions
|
@ -119,6 +119,7 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space)
|
||||||
}
|
}
|
||||||
|
|
||||||
out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : ""));
|
out += sprintf(out, "%s%s", ptype, (urbp->fsbr ? " FSBR" : ""));
|
||||||
|
out += sprintf(out, " Actlen=%d", urbp->urb->actual_length);
|
||||||
|
|
||||||
if (urbp->urb->status != -EINPROGRESS)
|
if (urbp->urb->status != -EINPROGRESS)
|
||||||
out += sprintf(out, " Status=%d", urbp->urb->status);
|
out += sprintf(out, " Status=%d", urbp->urb->status);
|
||||||
|
|
|
@ -129,6 +129,7 @@ struct uhci_qh {
|
||||||
struct list_head queue; /* Queue of urbps for this QH */
|
struct list_head queue; /* Queue of urbps for this QH */
|
||||||
struct uhci_qh *skel; /* Skeleton for this QH */
|
struct uhci_qh *skel; /* Skeleton for this QH */
|
||||||
struct uhci_td *dummy_td; /* Dummy TD to end the queue */
|
struct uhci_td *dummy_td; /* Dummy TD to end the queue */
|
||||||
|
struct uhci_td *post_td; /* Last TD completed */
|
||||||
|
|
||||||
unsigned int unlink_frame; /* When the QH was unlinked */
|
unsigned int unlink_frame; /* When the QH was unlinked */
|
||||||
int state; /* QH_STATE_xxx; see above */
|
int state; /* QH_STATE_xxx; see above */
|
||||||
|
@ -136,7 +137,7 @@ struct uhci_qh {
|
||||||
|
|
||||||
unsigned int initial_toggle:1; /* Endpoint's current toggle value */
|
unsigned int initial_toggle:1; /* Endpoint's current toggle value */
|
||||||
unsigned int needs_fixup:1; /* Must fix the TD toggle values */
|
unsigned int needs_fixup:1; /* Must fix the TD toggle values */
|
||||||
unsigned int is_stopped:1; /* Queue was stopped by an error */
|
unsigned int is_stopped:1; /* Queue was stopped by error/unlink */
|
||||||
} __attribute__((aligned(16)));
|
} __attribute__((aligned(16)));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -456,8 +457,6 @@ struct urb_priv {
|
||||||
struct list_head td_list;
|
struct list_head td_list;
|
||||||
|
|
||||||
unsigned fsbr : 1; /* URB turned on FSBR */
|
unsigned fsbr : 1; /* URB turned on FSBR */
|
||||||
unsigned short_transfer : 1; /* URB got a short transfer, no
|
|
||||||
* need to rescan */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -161,6 +161,7 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
|
||||||
if (!qh)
|
if (!qh)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
memset(qh, 0, sizeof(*qh));
|
||||||
qh->dma_handle = dma_handle;
|
qh->dma_handle = dma_handle;
|
||||||
|
|
||||||
qh->element = UHCI_PTR_TERM;
|
qh->element = UHCI_PTR_TERM;
|
||||||
|
@ -183,7 +184,6 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
|
||||||
|
|
||||||
} else { /* Skeleton QH */
|
} else { /* Skeleton QH */
|
||||||
qh->state = QH_STATE_ACTIVE;
|
qh->state = QH_STATE_ACTIVE;
|
||||||
qh->udev = NULL;
|
|
||||||
qh->type = -1;
|
qh->type = -1;
|
||||||
}
|
}
|
||||||
return qh;
|
return qh;
|
||||||
|
@ -223,16 +223,10 @@ static void uhci_save_toggle(struct uhci_qh *qh, struct urb *urb)
|
||||||
qh->type == USB_ENDPOINT_XFER_INT))
|
qh->type == USB_ENDPOINT_XFER_INT))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Find the first active TD; that's the device's toggle state */
|
WARN_ON(list_empty(&urbp->td_list));
|
||||||
list_for_each_entry(td, &urbp->td_list, list) {
|
td = list_entry(urbp->td_list.next, struct uhci_td, list);
|
||||||
if (td_status(td) & TD_CTRL_ACTIVE) {
|
qh->needs_fixup = 1;
|
||||||
qh->needs_fixup = 1;
|
qh->initial_toggle = uhci_toggle(td_token(td));
|
||||||
qh->initial_toggle = uhci_toggle(td_token(td));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN_ON(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -372,6 +366,12 @@ static void uhci_make_qh_idle(struct uhci_hcd *uhci, struct uhci_qh *qh)
|
||||||
list_move(&qh->node, &uhci->idle_qh_list);
|
list_move(&qh->node, &uhci->idle_qh_list);
|
||||||
qh->state = QH_STATE_IDLE;
|
qh->state = QH_STATE_IDLE;
|
||||||
|
|
||||||
|
/* Now that the QH is idle, its post_td isn't being used */
|
||||||
|
if (qh->post_td) {
|
||||||
|
uhci_free_td(uhci, qh->post_td);
|
||||||
|
qh->post_td = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* If anyone is waiting for a QH to become idle, wake them up */
|
/* If anyone is waiting for a QH to become idle, wake them up */
|
||||||
if (uhci->num_waiting)
|
if (uhci->num_waiting)
|
||||||
wake_up_all(&uhci->waitqh);
|
wake_up_all(&uhci->waitqh);
|
||||||
|
@ -610,6 +610,8 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb,
|
||||||
qh->skel = uhci->skel_fs_control_qh;
|
qh->skel = uhci->skel_fs_control_qh;
|
||||||
uhci_inc_fsbr(uhci, urb);
|
uhci_inc_fsbr(uhci, urb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
urb->actual_length = -8; /* Account for the SETUP packet */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
nomem:
|
nomem:
|
||||||
|
@ -767,34 +769,46 @@ static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
|
||||||
* Fix up the data structures following a short transfer
|
* Fix up the data structures following a short transfer
|
||||||
*/
|
*/
|
||||||
static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
|
static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
|
||||||
struct uhci_qh *qh, struct urb_priv *urbp,
|
struct uhci_qh *qh, struct urb_priv *urbp)
|
||||||
struct uhci_td *short_td)
|
|
||||||
{
|
{
|
||||||
struct uhci_td *td;
|
struct uhci_td *td;
|
||||||
int ret = 0;
|
struct list_head *tmp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
td = list_entry(urbp->td_list.prev, struct uhci_td, list);
|
td = list_entry(urbp->td_list.prev, struct uhci_td, list);
|
||||||
if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
|
if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
|
||||||
urbp->short_transfer = 1;
|
|
||||||
|
|
||||||
/* When a control transfer is short, we have to restart
|
/* When a control transfer is short, we have to restart
|
||||||
* the queue at the status stage transaction, which is
|
* the queue at the status stage transaction, which is
|
||||||
* the last TD. */
|
* the last TD. */
|
||||||
|
WARN_ON(list_empty(&urbp->td_list));
|
||||||
qh->element = cpu_to_le32(td->dma_handle);
|
qh->element = cpu_to_le32(td->dma_handle);
|
||||||
|
tmp = td->list.prev;
|
||||||
ret = -EINPROGRESS;
|
ret = -EINPROGRESS;
|
||||||
|
|
||||||
} else if (!urbp->short_transfer) {
|
} else {
|
||||||
urbp->short_transfer = 1;
|
|
||||||
|
|
||||||
/* When a bulk/interrupt transfer is short, we have to
|
/* When a bulk/interrupt transfer is short, we have to
|
||||||
* fix up the toggles of the following URBs on the queue
|
* fix up the toggles of the following URBs on the queue
|
||||||
* before restarting the queue at the next URB. */
|
* before restarting the queue at the next URB. */
|
||||||
qh->initial_toggle = uhci_toggle(td_token(short_td)) ^ 1;
|
qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1;
|
||||||
uhci_fixup_toggles(qh, 1);
|
uhci_fixup_toggles(qh, 1);
|
||||||
|
|
||||||
|
if (list_empty(&urbp->td_list))
|
||||||
|
td = qh->post_td;
|
||||||
qh->element = td->link;
|
qh->element = td->link;
|
||||||
|
tmp = urbp->td_list.prev;
|
||||||
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove all the TDs we skipped over, from tmp back to the start */
|
||||||
|
while (tmp != &urbp->td_list) {
|
||||||
|
td = list_entry(tmp, struct uhci_td, list);
|
||||||
|
tmp = tmp->prev;
|
||||||
|
|
||||||
|
uhci_remove_td_from_urb(td);
|
||||||
|
list_add(&td->remove_list, &uhci->td_remove_list);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -805,29 +819,14 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
|
||||||
{
|
{
|
||||||
struct urb_priv *urbp = urb->hcpriv;
|
struct urb_priv *urbp = urb->hcpriv;
|
||||||
struct uhci_qh *qh = urbp->qh;
|
struct uhci_qh *qh = urbp->qh;
|
||||||
struct uhci_td *td;
|
struct uhci_td *td, *tmp;
|
||||||
struct list_head *tmp;
|
|
||||||
unsigned status;
|
unsigned status;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
tmp = urbp->td_list.next;
|
list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
|
||||||
|
|
||||||
if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
|
|
||||||
if (urbp->short_transfer)
|
|
||||||
tmp = urbp->td_list.prev;
|
|
||||||
else
|
|
||||||
urb->actual_length = -8; /* SETUP packet */
|
|
||||||
} else
|
|
||||||
urb->actual_length = 0;
|
|
||||||
|
|
||||||
|
|
||||||
while (tmp != &urbp->td_list) {
|
|
||||||
unsigned int ctrlstat;
|
unsigned int ctrlstat;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
td = list_entry(tmp, struct uhci_td, list);
|
|
||||||
tmp = tmp->next;
|
|
||||||
|
|
||||||
ctrlstat = td_status(td);
|
ctrlstat = td_status(td);
|
||||||
status = uhci_status_bits(ctrlstat);
|
status = uhci_status_bits(ctrlstat);
|
||||||
if (status & TD_CTRL_ACTIVE)
|
if (status & TD_CTRL_ACTIVE)
|
||||||
|
@ -862,6 +861,12 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
|
||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uhci_remove_td_from_urb(td);
|
||||||
|
if (qh->post_td)
|
||||||
|
list_add(&qh->post_td->remove_list,
|
||||||
|
&uhci->td_remove_list);
|
||||||
|
qh->post_td = td;
|
||||||
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
@ -882,7 +887,7 @@ err:
|
||||||
(ret == -EREMOTEIO);
|
(ret == -EREMOTEIO);
|
||||||
|
|
||||||
} else /* Short packet received */
|
} else /* Short packet received */
|
||||||
ret = uhci_fixup_short_transfer(uhci, qh, urbp, td);
|
ret = uhci_fixup_short_transfer(uhci, qh, urbp);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1123,6 +1128,7 @@ __acquires(uhci->lock)
|
||||||
struct uhci_td *ptd, *ltd;
|
struct uhci_td *ptd, *ltd;
|
||||||
|
|
||||||
purbp = list_entry(urbp->node.prev, struct urb_priv, node);
|
purbp = list_entry(urbp->node.prev, struct urb_priv, node);
|
||||||
|
WARN_ON(list_empty(&purbp->td_list));
|
||||||
ptd = list_entry(purbp->td_list.prev, struct uhci_td,
|
ptd = list_entry(purbp->td_list.prev, struct uhci_td,
|
||||||
list);
|
list);
|
||||||
ltd = list_entry(urbp->td_list.prev, struct uhci_td,
|
ltd = list_entry(urbp->td_list.prev, struct uhci_td,
|
||||||
|
|
Loading…
Add table
Reference in a new issue