]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - drivers/firewire/ohci.c
firewire: ohci: handle receive packets with a data length of zero
[linux-3.10.git] / drivers / firewire / ohci.c
index ecddd11b797a366d105763656dd4eda2c2273add..553c74e1e4e3f876155d8511ef86b04d9ebb7c67 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/pci.h>
+#include <linux/pci_ids.h>
 #include <linux/spinlock.h>
 #include <linux/string.h>
 
@@ -204,7 +205,7 @@ struct fw_ohci {
        dma_addr_t config_rom_bus;
        __be32 *next_config_rom;
        dma_addr_t next_config_rom_bus;
-       u32 next_header;
+       __be32 next_header;
 
        struct ar_context ar_request_ctx;
        struct ar_context ar_response_ctx;
@@ -994,7 +995,8 @@ static int at_context_queue_packet(struct context *ctx,
                        packet->ack = RCODE_SEND_ERROR;
                        return -1;
                }
-               packet->payload_bus = payload_bus;
+               packet->payload_bus     = payload_bus;
+               packet->payload_mapped  = true;
 
                d[2].req_count    = cpu_to_le16(packet->payload_length);
                d[2].data_address = cpu_to_le32(payload_bus);
@@ -1022,7 +1024,7 @@ static int at_context_queue_packet(struct context *ctx,
         */
        if (ohci->generation != packet->generation ||
            reg_read(ohci, OHCI1394_IntEventSet) & OHCI1394_busReset) {
-               if (packet->payload_length > 0)
+               if (packet->payload_mapped)
                        dma_unmap_single(ohci->card.device, payload_bus,
                                         packet->payload_length, DMA_TO_DEVICE);
                packet->ack = RCODE_GENERATION;
@@ -1058,7 +1060,7 @@ static int handle_at_packet(struct context *context,
                /* This packet was cancelled, just continue. */
                return 1;
 
-       if (packet->payload_bus)
+       if (packet->payload_mapped)
                dma_unmap_single(ohci->card.device, packet->payload_bus,
                                 packet->payload_length, DMA_TO_DEVICE);
 
@@ -1278,8 +1280,8 @@ static void bus_reset_tasklet(unsigned long data)
         * the inverted quadlets and a header quadlet, we shift one
         * bit extra to get the actual number of self IDs.
         */
-       self_id_count = (reg >> 3) & 0x3ff;
-       if (self_id_count == 0) {
+       self_id_count = (reg >> 3) & 0xff;
+       if (self_id_count == 0 || self_id_count > 252) {
                fw_notify("inconsistent self IDs\n");
                return;
        }
@@ -1354,8 +1356,9 @@ static void bus_reset_tasklet(unsigned long data)
                 */
                reg_write(ohci, OHCI1394_BusOptions,
                          be32_to_cpu(ohci->config_rom[2]));
-               ohci->config_rom[0] = cpu_to_be32(ohci->next_header);
-               reg_write(ohci, OHCI1394_ConfigROMhdr, ohci->next_header);
+               ohci->config_rom[0] = ohci->next_header;
+               reg_write(ohci, OHCI1394_ConfigROMhdr,
+                         be32_to_cpu(ohci->next_header));
        }
 
 #ifdef CONFIG_FIREWIRE_OHCI_REMOTE_DMA
@@ -1463,7 +1466,17 @@ static int software_reset(struct fw_ohci *ohci)
        return -EBUSY;
 }
 
-static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
+static void copy_config_rom(__be32 *dest, const __be32 *src, size_t length)
+{
+       size_t size = length * 4;
+
+       memcpy(dest, src, size);
+       if (size < CONFIG_ROM_SIZE)
+               memset(&dest[length], 0, CONFIG_ROM_SIZE - size);
+}
+
+static int ohci_enable(struct fw_card *card,
+                      const __be32 *config_rom, size_t length)
 {
        struct fw_ohci *ohci = fw_ohci(card);
        struct pci_dev *dev = to_pci_dev(card->device);
@@ -1564,8 +1577,7 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
                if (ohci->next_config_rom == NULL)
                        return -ENOMEM;
 
-               memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE);
-               fw_memcpy_to_be32(ohci->next_config_rom, config_rom, length * 4);
+               copy_config_rom(ohci->next_config_rom, config_rom, length);
        } else {
                /*
                 * In the suspend case, config_rom is NULL, which
@@ -1575,7 +1587,7 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
                ohci->next_config_rom_bus = ohci->config_rom_bus;
        }
 
-       ohci->next_header = be32_to_cpu(ohci->next_config_rom[0]);
+       ohci->next_header = ohci->next_config_rom[0];
        ohci->next_config_rom[0] = 0;
        reg_write(ohci, OHCI1394_ConfigROMhdr, 0);
        reg_write(ohci, OHCI1394_BusOptions,
@@ -1609,7 +1621,7 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
 }
 
 static int ohci_set_config_rom(struct fw_card *card,
-                              u32 *config_rom, size_t length)
+                              const __be32 *config_rom, size_t length)
 {
        struct fw_ohci *ohci;
        unsigned long flags;
@@ -1658,9 +1670,7 @@ static int ohci_set_config_rom(struct fw_card *card,
                ohci->next_config_rom = next_config_rom;
                ohci->next_config_rom_bus = next_config_rom_bus;
 
-               memset(ohci->next_config_rom, 0, CONFIG_ROM_SIZE);
-               fw_memcpy_to_be32(ohci->next_config_rom, config_rom,
-                                 length * 4);
+               copy_config_rom(ohci->next_config_rom, config_rom, length);
 
                ohci->next_header = config_rom[0];
                ohci->next_config_rom[0] = 0;
@@ -1714,7 +1724,7 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet)
        if (packet->ack != 0)
                goto out;
 
-       if (packet->payload_bus)
+       if (packet->payload_mapped)
                dma_unmap_single(ohci->card.device, packet->payload_bus,
                                 packet->payload_length, DMA_TO_DEVICE);
 
@@ -2179,6 +2189,13 @@ static int ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
        page     = payload >> PAGE_SHIFT;
        offset   = payload & ~PAGE_MASK;
        rest     = p->payload_length;
+       /*
+        * The controllers I've tested have not worked correctly when
+        * second_req_count is zero.  Rather than do something we know won't
+        * work, return an error
+        */
+       if (rest == 0)
+               return -EINVAL;
 
        /* FIXME: make packet-per-buffer/dual-buffer a context option */
        while (rest > 0) {
@@ -2232,7 +2249,7 @@ static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
                                        unsigned long payload)
 {
        struct iso_context *ctx = container_of(base, struct iso_context, base);
-       struct descriptor *d = NULL, *pd = NULL;
+       struct descriptor *d, *pd;
        struct fw_iso_packet *p = packet;
        dma_addr_t d_bus, page_bus;
        u32 z, header_z, rest;
@@ -2270,8 +2287,9 @@ static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
                d->data_address = cpu_to_le32(d_bus + (z * sizeof(*d)));
 
                rest = payload_per_buffer;
+               pd = d;
                for (j = 1; j < z; j++) {
-                       pd = d + j;
+                       pd++;
                        pd->control = cpu_to_le16(DESCRIPTOR_STATUS |
                                                  DESCRIPTOR_INPUT_MORE);
 
@@ -2372,6 +2390,9 @@ static void ohci_pmac_off(struct pci_dev *dev)
 #define ohci_pmac_off(dev)
 #endif /* CONFIG_PPC_PMAC */
 
+#define PCI_VENDOR_ID_AGERE            PCI_VENDOR_ID_ATT
+#define PCI_DEVICE_ID_AGERE_FW643      0x5901
+
 static int __devinit pci_probe(struct pci_dev *dev,
                               const struct pci_device_id *ent)
 {
@@ -2422,6 +2443,16 @@ static int __devinit pci_probe(struct pci_dev *dev,
        version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
        ohci->use_dualbuffer = version >= OHCI_VERSION_1_1;
 
+       /* dual-buffer mode is broken if more than one IR context is active */
+       if (dev->vendor == PCI_VENDOR_ID_AGERE &&
+           dev->device == PCI_DEVICE_ID_AGERE_FW643)
+               ohci->use_dualbuffer = false;
+
+       /* dual-buffer mode is broken */
+       if (dev->vendor == PCI_VENDOR_ID_RICOH &&
+           dev->device == PCI_DEVICE_ID_RICOH_R5C832)
+               ohci->use_dualbuffer = false;
+
 /* x86-32 currently doesn't use highmem for dma_alloc_coherent */
 #if !defined(CONFIG_X86_32)
        /* dual-buffer mode is broken with descriptor addresses above 2G */