mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 05:02:12 +00:00
[PATCH] UHCI: Common result routine for Control/Bulk/Interrupt
This patch (as679) combines the result routine for Control URBs with the routine for Bulk/Interrupt URBs. Along the way I eliminated the debugging printouts for Control transfers unless the debugging level is set higher than 1. I also eliminated a long-unused (#ifdef'ed-out) section that works around some buggy old APC BackUPS devices. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
0f28b55db5
commit
b1869000a6
@ -618,134 +618,6 @@ nomem:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common submit for bulk and interrupt
|
||||
*/
|
||||
@ -864,86 +736,6 @@ nomem:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common result for bulk and interrupt
|
||||
*/
|
||||
static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
|
||||
{
|
||||
struct urb_priv *urbp = urb->hcpriv;
|
||||
struct uhci_td *td;
|
||||
unsigned int status = 0;
|
||||
int ret = 0;
|
||||
|
||||
urb->actual_length = 0;
|
||||
|
||||
list_for_each_entry(td, &urbp->td_list, list) {
|
||||
unsigned int 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;
|
||||
|
||||
if (uhci_actual_length(ctrlstat) <
|
||||
uhci_expected_length(td_token(td))) {
|
||||
if (urb->transfer_flags & URB_SHORT_NOT_OK) {
|
||||
ret = -EREMOTEIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* This URB stopped short of its end. We have to
|
||||
* fix up the toggles of the following URBs on the
|
||||
* queue and restart the queue.
|
||||
*
|
||||
* Do this only the first time we encounter the
|
||||
* short URB.
|
||||
*/
|
||||
if (!urbp->short_transfer) {
|
||||
urbp->short_transfer = 1;
|
||||
urbp->qh->initial_toggle =
|
||||
uhci_toggle(td_token(td)) ^ 1;
|
||||
uhci_fixup_toggles(urbp->qh, 1);
|
||||
|
||||
td = list_entry(urbp->td_list.prev,
|
||||
struct uhci_td, list);
|
||||
urbp->qh->element = td->link;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
td_error:
|
||||
ret = uhci_map_status(status, uhci_packetout(td_token(td)));
|
||||
|
||||
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
|
||||
/* Some debugging code */
|
||||
dev_dbg(uhci_dev(uhci), "%s: failed with status %x\n",
|
||||
__FUNCTION__, status);
|
||||
|
||||
if (debug > 1 && errbuf) {
|
||||
/* Print the chain for debugging purposes */
|
||||
uhci_show_qh(urbp->qh, errbuf, ERRBUF_LEN, 0);
|
||||
lprintk(errbuf);
|
||||
}
|
||||
}
|
||||
err:
|
||||
|
||||
/* Note that the queue has stopped and save the next toggle value */
|
||||
urbp->qh->element = UHCI_PTR_TERM;
|
||||
urbp->qh->is_stopped = 1;
|
||||
urbp->qh->needs_fixup = 1;
|
||||
urbp->qh->initial_toggle = uhci_toggle(td_token(td)) ^
|
||||
(ret == -EREMOTEIO);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb,
|
||||
struct uhci_qh *qh)
|
||||
{
|
||||
@ -971,6 +763,129 @@ static inline int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb,
|
||||
return uhci_submit_common(uhci, urb, qh);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix up the data structures following a short transfer
|
||||
*/
|
||||
static int uhci_fixup_short_transfer(struct uhci_hcd *uhci,
|
||||
struct uhci_qh *qh, struct urb_priv *urbp,
|
||||
struct uhci_td *short_td)
|
||||
{
|
||||
struct uhci_td *td;
|
||||
int ret = 0;
|
||||
|
||||
td = list_entry(urbp->td_list.prev, struct uhci_td, list);
|
||||
if (qh->type == USB_ENDPOINT_XFER_CONTROL) {
|
||||
urbp->short_transfer = 1;
|
||||
|
||||
/* When a control transfer is short, we have to restart
|
||||
* the queue at the status stage transaction, which is
|
||||
* the last TD. */
|
||||
qh->element = cpu_to_le32(td->dma_handle);
|
||||
ret = -EINPROGRESS;
|
||||
|
||||
} else if (!urbp->short_transfer) {
|
||||
urbp->short_transfer = 1;
|
||||
|
||||
/* When a bulk/interrupt transfer is short, we have to
|
||||
* fix up the toggles of the following URBs on the queue
|
||||
* before restarting the queue at the next URB. */
|
||||
qh->initial_toggle = uhci_toggle(td_token(short_td)) ^ 1;
|
||||
uhci_fixup_toggles(qh, 1);
|
||||
|
||||
qh->element = td->link;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common result for control, bulk, and interrupt
|
||||
*/
|
||||
static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb)
|
||||
{
|
||||
struct urb_priv *urbp = urb->hcpriv;
|
||||
struct uhci_qh *qh = urbp->qh;
|
||||
struct uhci_td *td;
|
||||
struct list_head *tmp;
|
||||
unsigned status;
|
||||
int ret = 0;
|
||||
|
||||
tmp = urbp->td_list.next;
|
||||
|
||||
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;
|
||||
int len;
|
||||
|
||||
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;
|
||||
|
||||
len = uhci_actual_length(ctrlstat);
|
||||
urb->actual_length += len;
|
||||
|
||||
if (status) {
|
||||
ret = uhci_map_status(status,
|
||||
uhci_packetout(td_token(td)));
|
||||
if ((debug == 1 && ret != -EPIPE) || debug > 1) {
|
||||
/* Some debugging code */
|
||||
dev_dbg(uhci_dev(uhci),
|
||||
"%s: failed with status %x\n",
|
||||
__FUNCTION__, status);
|
||||
|
||||
if (debug > 1 && errbuf) {
|
||||
/* Print the chain for debugging */
|
||||
uhci_show_qh(urbp->qh, errbuf,
|
||||
ERRBUF_LEN, 0);
|
||||
lprintk(errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (len < uhci_expected_length(td_token(td))) {
|
||||
|
||||
/* We received a short packet */
|
||||
if (urb->transfer_flags & URB_SHORT_NOT_OK)
|
||||
ret = -EREMOTEIO;
|
||||
else if (ctrlstat & TD_CTRL_SPD)
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
}
|
||||
return ret;
|
||||
|
||||
err:
|
||||
if (ret < 0) {
|
||||
/* In case a control transfer gets an error
|
||||
* during the setup stage */
|
||||
urb->actual_length = max(urb->actual_length, 0);
|
||||
|
||||
/* Note that the queue has stopped and save
|
||||
* the next toggle value */
|
||||
qh->element = UHCI_PTR_TERM;
|
||||
qh->is_stopped = 1;
|
||||
qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL);
|
||||
qh->initial_toggle = uhci_toggle(td_token(td)) ^
|
||||
(ret == -EREMOTEIO);
|
||||
|
||||
} else /* Short packet received */
|
||||
ret = uhci_fixup_short_transfer(uhci, qh, urbp, td);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Isochronous transfers
|
||||
*/
|
||||
@ -1276,17 +1191,10 @@ static void uhci_scan_qh(struct uhci_hcd *uhci, struct uhci_qh *qh,
|
||||
urbp = list_entry(qh->queue.next, struct urb_priv, node);
|
||||
urb = urbp->urb;
|
||||
|
||||
switch (qh->type) {
|
||||
case USB_ENDPOINT_XFER_CONTROL:
|
||||
status = uhci_result_control(uhci, urb);
|
||||
break;
|
||||
case USB_ENDPOINT_XFER_ISOC:
|
||||
if (qh->type == USB_ENDPOINT_XFER_ISOC)
|
||||
status = uhci_result_isochronous(uhci, urb);
|
||||
break;
|
||||
default: /* USB_ENDPOINT_XFER_BULK or _INT */
|
||||
else
|
||||
status = uhci_result_common(uhci, urb);
|
||||
break;
|
||||
}
|
||||
if (status == -EINPROGRESS)
|
||||
break;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user