Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 59e29ed9 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

[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: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent b1869000
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -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);
+2 −3
Original line number Original line Diff line number Diff line
@@ -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 */
};
};




+42 −36
Original line number Original line Diff line number Diff line
@@ -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 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
				(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,