firewire: Implement sync and tag matching for isochronous receive.

Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
diff --git a/drivers/firewire/fw-device-cdev.c b/drivers/firewire/fw-device-cdev.c
index 5c87618..33fe576 100644
--- a/drivers/firewire/fw-device-cdev.c
+++ b/drivers/firewire/fw-device-cdev.c
@@ -416,6 +416,12 @@
 	if (request.channel > 63)
 		return -EINVAL;
 
+	if (request.sync > 15)
+		return -EINVAL;
+
+	if (request.tags == 0 || request.tags > 15)
+		return -EINVAL;
+
 	if (request.speed > SCODE_3200)
 		return -EINVAL;
 
@@ -424,6 +430,8 @@
 						    request.channel,
 						    request.speed,
 						    request.header_size,
+						    request.sync,
+						    request.tags,
 						    iso_callback, client);
 	if (IS_ERR(client->iso_context))
 		return PTR_ERR(client->iso_context);
@@ -495,7 +503,7 @@
 		if (__copy_from_user
 		    (u.packet.header, p->header, header_length))
 			return -EFAULT;
-		if (u.packet.skip &&
+		if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
 		    u.packet.header_length + u.packet.payload_length > 0)
 			return -EINVAL;
 		if (payload + u.packet.payload_length > payload_end)
diff --git a/drivers/firewire/fw-device-cdev.h b/drivers/firewire/fw-device-cdev.h
index 99e6aa6..739f54f 100644
--- a/drivers/firewire/fw-device-cdev.h
+++ b/drivers/firewire/fw-device-cdev.h
@@ -131,11 +131,19 @@
 #define FW_CDEV_ISO_CONTEXT_TRANSMIT	0
 #define FW_CDEV_ISO_CONTEXT_RECEIVE	1
 
+#define FW_CDEV_ISO_CONTEXT_MATCH_TAG0		 1
+#define FW_CDEV_ISO_CONTEXT_MATCH_TAG1		 2
+#define FW_CDEV_ISO_CONTEXT_MATCH_TAG2		 4
+#define FW_CDEV_ISO_CONTEXT_MATCH_TAG3		 8
+#define FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS	15
+
 struct fw_cdev_create_iso_context {
 	__u32 type;
 	__u32 header_size;
 	__u32 channel;
 	__u32 speed;
+	__u32 sync;
+	__u32 tags;
 };
 
 struct fw_cdev_iso_packet {
diff --git a/drivers/firewire/fw-iso.c b/drivers/firewire/fw-iso.c
index dc5a7e3..728cbb3 100644
--- a/drivers/firewire/fw-iso.c
+++ b/drivers/firewire/fw-iso.c
@@ -107,12 +107,14 @@
 
 struct fw_iso_context *
 fw_iso_context_create(struct fw_card *card, int type,
-		      int channel, int speed, size_t header_size,
+		      int channel, int speed,
+		      int sync, int tags, size_t header_size,
 		      fw_iso_callback_t callback, void *callback_data)
 {
 	struct fw_iso_context *ctx;
 
-	ctx = card->driver->allocate_iso_context(card, type, header_size);
+	ctx = card->driver->allocate_iso_context(card, type,
+						 sync, tags, header_size);
 	if (IS_ERR(ctx))
 		return ctx;
 
@@ -120,6 +122,8 @@
 	ctx->type = type;
 	ctx->channel = channel;
 	ctx->speed = speed;
+	ctx->sync = sync;
+	ctx->tags = tags;
 	ctx->header_size = header_size;
 	ctx->callback = callback;
 	ctx->callback_data = callback_data;
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c
index 0088acd..ea43a5e 100644
--- a/drivers/firewire/fw-ohci.c
+++ b/drivers/firewire/fw-ohci.c
@@ -1337,7 +1337,8 @@
 }
 
 static struct fw_iso_context *
-ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
+ohci_allocate_iso_context(struct fw_card *card, int type,
+			  int sync, int tags, size_t header_size)
 {
 	struct fw_ohci *ohci = fw_ohci(card);
 	struct iso_context *ctx, *list;
@@ -1427,7 +1428,8 @@
 		reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 1 << index);
 		reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1 << index);
 		reg_write(ohci, context_match(ctx->context.regs),
-			  0xf0000000 | ctx->base.channel);
+			  (ctx->base.tags << 28) |
+			  (ctx->base.sync << 8) | ctx->base.channel);
 		context_run(&ctx->context, mode);
 	}
 
@@ -1573,6 +1575,26 @@
 
 	return 0;
 }
+ 
+static int
+setup_wait_descriptor(struct context *ctx)
+{
+	struct descriptor *d;
+	dma_addr_t d_bus;
+
+	d = context_get_descriptors(ctx, 1, &d_bus);
+	if (d == NULL)
+		return -ENOMEM;
+
+	d->control = cpu_to_le16(descriptor_input_more |
+				 descriptor_status |
+				 descriptor_branch_always |
+				 descriptor_wait);
+
+	context_append(ctx, d, 1, 0);
+
+	return 0;
+}
 
 static int
 ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
@@ -1591,6 +1613,9 @@
 	/* FIXME: Cycle lost behavior should be configurable: lose
 	 * packet, retransmit or terminate.. */
 
+	if (packet->skip && setup_wait_descriptor(&ctx->context) < 0)
+		return -ENOMEM;
+
 	p = packet;
 	z = 2;
 
@@ -1655,6 +1680,9 @@
 	offset = payload & ~PAGE_MASK;
 	rest   = packet->payload_length;
 
+	if (packet->skip && setup_wait_descriptor(&ctx->context) < 0)
+		return -ENOMEM;
+
 	while (rest > 0) {
 		d = context_get_descriptors(&ctx->context, 1, &d_bus);
 		if (d == NULL)
diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h
index 22e45cc..cbea845 100644
--- a/drivers/firewire/fw-transaction.h
+++ b/drivers/firewire/fw-transaction.h
@@ -332,6 +332,12 @@
 #define FW_ISO_CONTEXT_TRANSMIT	0
 #define FW_ISO_CONTEXT_RECEIVE	1
 
+#define FW_ISO_CONTEXT_MATCH_TAG0	 1
+#define FW_ISO_CONTEXT_MATCH_TAG1	 2
+#define FW_ISO_CONTEXT_MATCH_TAG2	 4
+#define FW_ISO_CONTEXT_MATCH_TAG3	 8
+#define FW_ISO_CONTEXT_MATCH_ALL_TAGS	15
+
 struct fw_iso_context;
 
 typedef void (*fw_iso_callback_t) (struct fw_iso_context *context,
@@ -357,6 +363,8 @@
 	int type;
 	int channel;
 	int speed;
+	int sync;
+	int tags;
 	size_t header_size;
 	fw_iso_callback_t callback;
 	void *callback_data;
@@ -374,7 +382,8 @@
 
 struct fw_iso_context *
 fw_iso_context_create(struct fw_card *card, int type,
-		      int channel, int speed, size_t header_size,
+		      int channel, int speed,
+		      int sync, int tags, size_t header_size,
 		      fw_iso_callback_t callback, void *callback_data);
 
 void
@@ -425,7 +434,7 @@
 				int node_id, int generation);
 
 	struct fw_iso_context *
-	(*allocate_iso_context)(struct fw_card *card,
+	(*allocate_iso_context)(struct fw_card *card, int sync, int tags,
 				int type, size_t header_size);
 	void (*free_iso_context)(struct fw_iso_context *ctx);