Skip to content

Commit

Permalink
firewire: prevent creation of multiple IR DMA contexts for the same c…
Browse files Browse the repository at this point in the history
…hannel

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>
  • Loading branch information
Stefan Richter committed Mar 24, 2009
1 parent a459b8a commit 4817ed2
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 5 deletions.
3 changes: 2 additions & 1 deletion drivers/firewire/fw-iso.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
{
struct fw_iso_context *ctx;

ctx = card->driver->allocate_iso_context(card, type, header_size);
ctx = card->driver->allocate_iso_context(card,
type, channel, header_size);
if (IS_ERR(ctx))
return ctx;

Expand Down
14 changes: 11 additions & 3 deletions drivers/firewire/fw-ohci.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ struct fw_ohci {

u32 it_context_mask;
struct iso_context *it_context_list;
u64 ir_context_channels;
u32 ir_context_mask;
struct iso_context *ir_context_list;
};
Expand Down Expand Up @@ -1877,20 +1878,23 @@ static int handle_it_packet(struct context *context,
}

static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
int type, size_t header_size)
int type, int channel, size_t header_size)
{
struct fw_ohci *ohci = fw_ohci(card);
struct iso_context *ctx, *list;
descriptor_callback_t callback;
u64 *channels, dont_care = ~0ULL;
u32 *mask, regs;
unsigned long flags;
int index, ret = -ENOMEM;

if (type == FW_ISO_CONTEXT_TRANSMIT) {
channels = &dont_care;
mask = &ohci->it_context_mask;
list = ohci->it_context_list;
callback = handle_it_packet;
} else {
channels = &ohci->ir_context_channels;
mask = &ohci->ir_context_mask;
list = ohci->ir_context_list;
if (ohci->use_dualbuffer)
Expand All @@ -1900,9 +1904,11 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
}

spin_lock_irqsave(&ohci->lock, flags);
index = ffs(*mask) - 1;
if (index >= 0)
index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1;
if (index >= 0) {
*channels &= ~(1ULL << channel);
*mask &= ~(1 << index);
}
spin_unlock_irqrestore(&ohci->lock, flags);

if (index < 0)
Expand Down Expand Up @@ -2012,6 +2018,7 @@ static void ohci_free_iso_context(struct fw_iso_context *base)
} else {
index = ctx - ohci->ir_context_list;
ohci->ir_context_mask |= 1 << index;
ohci->ir_context_channels |= 1ULL << base->channel;
}

spin_unlock_irqrestore(&ohci->lock, flags);
Expand Down Expand Up @@ -2424,6 +2431,7 @@ static int __devinit pci_probe(struct pci_dev *dev,
ohci->it_context_list = kzalloc(size, GFP_KERNEL);

reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0);
ohci->ir_context_channels = ~0ULL;
ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet);
reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0);
size = sizeof(struct iso_context) * hweight32(ohci->ir_context_mask);
Expand Down
2 changes: 1 addition & 1 deletion drivers/firewire/fw-transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ struct fw_card_driver {

struct fw_iso_context *
(*allocate_iso_context)(struct fw_card *card,
int type, size_t header_size);
int type, int channel, size_t header_size);
void (*free_iso_context)(struct fw_iso_context *ctx);

int (*start_iso)(struct fw_iso_context *ctx,
Expand Down

0 comments on commit 4817ed2

Please sign in to comment.