summaryrefslogtreecommitdiffstats
path: root/firmware
diff options
context:
space:
mode:
authorAmaury Pouly <pamaury@rockbox.org>2010-03-25 13:45:19 +0000
committerAmaury Pouly <pamaury@rockbox.org>2010-03-25 13:45:19 +0000
commit1dd216ba066ffcb433e09dfd5c5df3edadb4b578 (patch)
tree769923b21a8bad7748d0c4742fed00024c88eb5b /firmware
parenta4ff42d146668b38aa76597fd756e5d0b3b10e00 (diff)
downloadrockbox-1dd216ba066ffcb433e09dfd5c5df3edadb4b578.tar.gz
rockbox-1dd216ba066ffcb433e09dfd5c5df3edadb4b578.tar.bz2
rockbox-1dd216ba066ffcb433e09dfd5c5df3edadb4b578.zip
Fix usb-arc driver: the driver would prematurely mark a transfer as complete whereas only a part of it actually is, check the active of the TDs to avoid that. This should fix some HID+UMS bugs.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25328 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
-rw-r--r--firmware/target/arm/usb-drv-arc.c21
1 files changed, 17 insertions, 4 deletions
diff --git a/firmware/target/arm/usb-drv-arc.c b/firmware/target/arm/usb-drv-arc.c
index 94dc9b7b5e..3b1146cc5c 100644
--- a/firmware/target/arm/usb-drv-arc.c
+++ b/firmware/target/arm/usb-drv-arc.c
@@ -917,20 +917,33 @@ static void transfer_completed(void)
int pipe = ep * 2 + dir;
if (mask & pipe2mask[pipe]) {
struct queue_head* qh = &qh_array[pipe];
- if(qh->wait) {
- qh->wait=0;
- wakeup_signal(&transfer_completion_signal[pipe]);
- }
+
int length=0;
struct transfer_descriptor* td=&td_array[pipe*NUM_TDS_PER_EP];
while(td!=(struct transfer_descriptor*)DTD_NEXT_TERMINATE && td!=0)
{
+ /* It seems that the controller sets the pipe bit to one even if the TD
+ * dosn't have the IOC bit set. So we have the rely the active status bit
+ * to check that all the TDs of the transfer are really finished and let
+ * the transfer continue if it's no the case */
+ if(td->size_ioc_sts & DTD_STATUS_ACTIVE)
+ {
+ logf("skip half finished transfer");
+ goto Lskip;
+ }
length += ((td->reserved & DTD_RESERVED_LENGTH_MASK) -
((td->size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS));
td=(struct transfer_descriptor*) td->next_td_ptr;
}
+ if(qh->wait) {
+ qh->wait=0;
+ wakeup_signal(&transfer_completion_signal[pipe]);
+ }
+
usb_core_transfer_complete(ep, dir?USB_DIR_IN:USB_DIR_OUT,
qh->status, length);
+ Lskip:
+ continue;
}
}
}