aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/fw-ohci.c
diff options
context:
space:
mode:
authorStefan Richter <stefanr@s5r6.in-berlin.de>2008-12-21 10:39:46 -0500
committerStefan Richter <stefanr@s5r6.in-berlin.de>2009-03-24 15:56:41 -0400
commit4817ed240232e89583b0506c2d8e426739af5da3 (patch)
tree95d8220f1cae2df2490488dd35381c7d73a5be7b /drivers/firewire/fw-ohci.c
parenta459b8ab9c176143fecef8ace4b70d6dbd7a8113 (diff)
firewire: prevent creation of multiple IR DMA contexts for the same channel
OHCI-1394 1.1 clause 10.4.3 says: "If more than one IR DMA context specifies receives for packets from the same isochronous channel, the context destination for that channel's packets is undefined." Any userspace client and in the future also kernelspace clients can allocate IR DMA contexts for any channel. We don't want them to interfere with each other, hence it is preferable to return -EBUSY if allocation of a second context for a channel is attempted. Notes: - This limitation is OHCI-1394 specific, therefore its proper place of implementation is down in the low-level driver. - Since the <linux/firewire-cdev.h> ABI simply maps one userspace iso client context to one hardware iso context, this OHCI-1394 limitation alas requires userspace to implement its own multiplexing of iso reception from the same channel and card to multiple clients when needed. - The limitation is independent of channel allocation at the IRM; the latter is really only important for the initiation of iso transmission but not of iso reception. - We don't need to do the same for IT DMA because OHCI-1394 does not have any ties between IT contexts and channels. Only the voluntary channel allocation protocol via the IRM, globally to the FireWire bus, can ensure proper isochronous transmit behaviour anyway. 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.c14
1 files changed, 11 insertions, 3 deletions
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c
index 4c7cf15986a..859af71b06a 100644
--- a/drivers/firewire/fw-ohci.c
+++ b/drivers/firewire/fw-ohci.c
@@ -205,6 +205,7 @@ struct fw_ohci {
205 205
206 u32 it_context_mask; 206 u32 it_context_mask;
207 struct iso_context *it_context_list; 207 struct iso_context *it_context_list;
208 u64 ir_context_channels;
208 u32 ir_context_mask; 209 u32 ir_context_mask;
209 struct iso_context *ir_context_list; 210 struct iso_context *ir_context_list;
210}; 211};
@@ -1877,20 +1878,23 @@ static int handle_it_packet(struct context *context,
1877} 1878}
1878 1879
1879static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card, 1880static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
1880 int type, size_t header_size) 1881 int type, int channel, size_t header_size)
1881{ 1882{
1882 struct fw_ohci *ohci = fw_ohci(card); 1883 struct fw_ohci *ohci = fw_ohci(card);
1883 struct iso_context *ctx, *list; 1884 struct iso_context *ctx, *list;
1884 descriptor_callback_t callback; 1885 descriptor_callback_t callback;
1886 u64 *channels, dont_care = ~0ULL;
1885 u32 *mask, regs; 1887 u32 *mask, regs;
1886 unsigned long flags; 1888 unsigned long flags;
1887 int index, ret = -ENOMEM; 1889 int index, ret = -ENOMEM;
1888 1890
1889 if (type == FW_ISO_CONTEXT_TRANSMIT) { 1891 if (type == FW_ISO_CONTEXT_TRANSMIT) {
1892 channels = &dont_care;
1890 mask = &ohci->it_context_mask; 1893 mask = &ohci->it_context_mask;
1891 list = ohci->it_context_list; 1894 list = ohci->it_context_list;
1892 callback = handle_it_packet; 1895 callback = handle_it_packet;
1893 } else { 1896 } else {
1897 channels = &ohci->ir_context_channels;
1894 mask = &ohci->ir_context_mask; 1898 mask = &ohci->ir_context_mask;
1895 list = ohci->ir_context_list; 1899 list = ohci->ir_context_list;
1896 if (ohci->use_dualbuffer) 1900 if (ohci->use_dualbuffer)
@@ -1900,9 +1904,11 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
1900 } 1904 }
1901 1905
1902 spin_lock_irqsave(&ohci->lock, flags); 1906 spin_lock_irqsave(&ohci->lock, flags);
1903 index = ffs(*mask) - 1; 1907 index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1;
1904 if (index >= 0) 1908 if (index >= 0) {
1909 *channels &= ~(1ULL << channel);
1905 *mask &= ~(1 << index); 1910 *mask &= ~(1 << index);
1911 }
1906 spin_unlock_irqrestore(&ohci->lock, flags); 1912 spin_unlock_irqrestore(&ohci->lock, flags);
1907 1913
1908 if (index < 0) 1914 if (index < 0)
@@ -2012,6 +2018,7 @@ static void ohci_free_iso_context(struct fw_iso_context *base)
2012 } else { 2018 } else {
2013 index = ctx - ohci->ir_context_list; 2019 index = ctx - ohci->ir_context_list;
2014 ohci->ir_context_mask |= 1 << index; 2020 ohci->ir_context_mask |= 1 << index;
2021 ohci->ir_context_channels |= 1ULL << base->channel;
2015 } 2022 }
2016 2023
2017 spin_unlock_irqrestore(&ohci->lock, flags); 2024 spin_unlock_irqrestore(&ohci->lock, flags);
@@ -2424,6 +2431,7 @@ static int __devinit pci_probe(struct pci_dev *dev,
2424 ohci->it_context_list = kzalloc(size, GFP_KERNEL); 2431 ohci->it_context_list = kzalloc(size, GFP_KERNEL);
2425 2432
2426 reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0); 2433 reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0);
2434 ohci->ir_context_channels = ~0ULL;
2427 ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet); 2435 ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet);
2428 reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0); 2436 reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0);
2429 size = sizeof(struct iso_context) * hweight32(ohci->ir_context_mask); 2437 size = sizeof(struct iso_context) * hweight32(ohci->ir_context_mask);