aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/fw-ohci.c
diff options
context:
space:
mode:
authorDavid Moore <dcm@MIT.EDU>2007-12-19 03:09:18 -0500
committerStefan Richter <stefanr@s5r6.in-berlin.de>2008-01-30 16:22:23 -0500
commit0642b6577f1d455ed99e2da4a4d9216a866d2449 (patch)
tree35ae292471403cfe0f1a6ef9dc4c7d24b16ff340 /drivers/firewire/fw-ohci.c
parent4b11ea96a08a0f97a16edba55a615354c6d846b5 (diff)
firewire: fw-ohci: Fix for dualbuffer three-or-more buffers
This patch fixes the problem where different OHCI 1.1 controllers behave differently when a received iso packet straddles three or more buffers when using the dual-buffer receive mode. Two changes are made in order to handle this situation: 1. The packet sync DMA descriptor is given a non-zero header length and non-zero payload length. This is because zero-payload descriptors are not discussed in the OHCI 1.1 specs and their behavior is thus undefined. Instead we use a header size just large enough for a single header and a payload length of 4 bytes for this first descriptor. 2. As we process received packets in the context's tasklet, read the packet length out of the headers. Keep track of the running total of the packet length as "excess_bytes", so we can ignore any descriptors where no packet starts or ends. These descriptors may not have had their first_res_count or second_res_count fields updated by the controller so we cannot rely on those values. The main drawback of this patch is that the excess_bytes value might get "out of sync" with the packet descriptors if something strange happens to the DMA program. I'm not if such a thing could ever happen, but I appreciate any suggestions in making it more robust. Also, the packet-per-buffer support may need a similar fix to deal with issue 1, but I haven't done any work on that yet. Stefan, I'm hoping that with this patch, all your OHCI 1.1 controllers will work properly with an unmodified version of libdc1394. Signed-off-by: David Moore <dcm@acm.org> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/fw-ohci.c')
-rw-r--r--drivers/firewire/fw-ohci.c44
1 files changed, 24 insertions, 20 deletions
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c
index 436a855a4c6..a44d16d0c50 100644
--- a/drivers/firewire/fw-ohci.c
+++ b/drivers/firewire/fw-ohci.c
@@ -125,6 +125,7 @@ struct context {
125struct iso_context { 125struct iso_context {
126 struct fw_iso_context base; 126 struct fw_iso_context base;
127 struct context context; 127 struct context context;
128 int excess_bytes;
128 void *header; 129 void *header;
129 size_t header_length; 130 size_t header_length;
130}; 131};
@@ -1408,9 +1409,13 @@ static int handle_ir_dualbuffer_packet(struct context *context,
1408 void *p, *end; 1409 void *p, *end;
1409 int i; 1410 int i;
1410 1411
1411 if (db->first_res_count > 0 && db->second_res_count > 0) 1412 if (db->first_res_count > 0 && db->second_res_count > 0) {
1412 /* This descriptor isn't done yet, stop iteration. */ 1413 if (ctx->excess_bytes <= le16_to_cpu(db->second_req_count)) {
1413 return 0; 1414 /* This descriptor isn't done yet, stop iteration. */
1415 return 0;
1416 }
1417 ctx->excess_bytes -= le16_to_cpu(db->second_req_count);
1418 }
1414 1419
1415 header_length = le16_to_cpu(db->first_req_count) - 1420 header_length = le16_to_cpu(db->first_req_count) -
1416 le16_to_cpu(db->first_res_count); 1421 le16_to_cpu(db->first_res_count);
@@ -1429,11 +1434,15 @@ static int handle_ir_dualbuffer_packet(struct context *context,
1429 *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4)); 1434 *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
1430 memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4); 1435 memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
1431 i += ctx->base.header_size; 1436 i += ctx->base.header_size;
1437 ctx->excess_bytes +=
1438 (le32_to_cpu(*(u32 *)(p + 4)) >> 16) & 0xffff;
1432 p += ctx->base.header_size + 4; 1439 p += ctx->base.header_size + 4;
1433 } 1440 }
1434
1435 ctx->header_length = i; 1441 ctx->header_length = i;
1436 1442
1443 ctx->excess_bytes -= le16_to_cpu(db->second_req_count) -
1444 le16_to_cpu(db->second_res_count);
1445
1437 if (le16_to_cpu(db->control) & DESCRIPTOR_IRQ_ALWAYS) { 1446 if (le16_to_cpu(db->control) & DESCRIPTOR_IRQ_ALWAYS) {
1438 ir_header = (__le32 *) (db + 1); 1447 ir_header = (__le32 *) (db + 1);
1439 ctx->base.callback(&ctx->base, 1448 ctx->base.callback(&ctx->base,
@@ -1775,19 +1784,6 @@ ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
1775 * packet, retransmit or terminate.. 1784 * packet, retransmit or terminate..
1776 */ 1785 */
1777 1786
1778 if (packet->skip) {
1779 d = context_get_descriptors(&ctx->context, 2, &d_bus);
1780 if (d == NULL)
1781 return -ENOMEM;
1782
1783 db = (struct db_descriptor *) d;
1784 db->control = cpu_to_le16(DESCRIPTOR_STATUS |
1785 DESCRIPTOR_BRANCH_ALWAYS |
1786 DESCRIPTOR_WAIT);
1787 db->first_size = cpu_to_le16(ctx->base.header_size + 4);
1788 context_append(&ctx->context, d, 2, 0);
1789 }
1790
1791 p = packet; 1787 p = packet;
1792 z = 2; 1788 z = 2;
1793 1789
@@ -1815,11 +1811,18 @@ ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
1815 db->control = cpu_to_le16(DESCRIPTOR_STATUS | 1811 db->control = cpu_to_le16(DESCRIPTOR_STATUS |
1816 DESCRIPTOR_BRANCH_ALWAYS); 1812 DESCRIPTOR_BRANCH_ALWAYS);
1817 db->first_size = cpu_to_le16(ctx->base.header_size + 4); 1813 db->first_size = cpu_to_le16(ctx->base.header_size + 4);
1818 db->first_req_count = cpu_to_le16(header_size); 1814 if (p->skip && rest == p->payload_length) {
1815 db->control |= cpu_to_le16(DESCRIPTOR_WAIT);
1816 db->first_req_count = db->first_size;
1817 } else {
1818 db->first_req_count = cpu_to_le16(header_size);
1819 }
1819 db->first_res_count = db->first_req_count; 1820 db->first_res_count = db->first_req_count;
1820 db->first_buffer = cpu_to_le32(d_bus + sizeof(*db)); 1821 db->first_buffer = cpu_to_le32(d_bus + sizeof(*db));
1821 1822
1822 if (offset + rest < PAGE_SIZE) 1823 if (p->skip && rest == p->payload_length)
1824 length = 4;
1825 else if (offset + rest < PAGE_SIZE)
1823 length = rest; 1826 length = rest;
1824 else 1827 else
1825 length = PAGE_SIZE - offset; 1828 length = PAGE_SIZE - offset;
@@ -1835,7 +1838,8 @@ ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
1835 context_append(&ctx->context, d, z, header_z); 1838 context_append(&ctx->context, d, z, header_z);
1836 offset = (offset + length) & ~PAGE_MASK; 1839 offset = (offset + length) & ~PAGE_MASK;
1837 rest -= length; 1840 rest -= length;
1838 page++; 1841 if (offset == 0)
1842 page++;
1839 } 1843 }
1840 1844
1841 return 0; 1845 return 0;