firewire: Implement functionality to stop isochronous DMA contexts.

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 b738c99..1ce33d4 100644
--- a/drivers/firewire/fw-device-cdev.c
+++ b/drivers/firewire/fw-device-cdev.c
@@ -514,6 +514,11 @@
 				    request.speed, request.cycle);
 }
 
+static int ioctl_stop_iso(struct client *client, void __user *arg)
+{
+	return fw_iso_context_stop(client->iso_context);
+}
+
 static int
 dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
 {
@@ -532,6 +537,8 @@
 		return ioctl_queue_iso(client, arg);
 	case FW_CDEV_IOC_START_ISO:
 		return ioctl_start_iso(client, arg);
+	case FW_CDEV_IOC_STOP_ISO:
+		return ioctl_stop_iso(client, arg);
 	default:
 		return -EINVAL;
 	}
diff --git a/drivers/firewire/fw-device-cdev.h b/drivers/firewire/fw-device-cdev.h
index ac91ce5..257dc87 100644
--- a/drivers/firewire/fw-device-cdev.h
+++ b/drivers/firewire/fw-device-cdev.h
@@ -98,6 +98,7 @@
 #define FW_CDEV_IOC_CREATE_ISO_CONTEXT	_IO('#', 0x04)
 #define FW_CDEV_IOC_QUEUE_ISO		_IO('#', 0x05)
 #define FW_CDEV_IOC_START_ISO		_IO('#', 0x06)
+#define FW_CDEV_IOC_STOP_ISO		_IO('#', 0x07)
 
 struct fw_cdev_get_config_rom {
 	__u32 length;
diff --git a/drivers/firewire/fw-iso.c b/drivers/firewire/fw-iso.c
index d84792f..1605e11 100644
--- a/drivers/firewire/fw-iso.c
+++ b/drivers/firewire/fw-iso.c
@@ -155,3 +155,10 @@
 	return card->driver->queue_iso(ctx, packet, buffer, payload);
 }
 EXPORT_SYMBOL(fw_iso_context_queue);
+
+int
+fw_iso_context_stop(struct fw_iso_context *ctx)
+{
+	return ctx->card->driver->stop_iso(ctx);
+}
+EXPORT_SYMBOL(fw_iso_context_stop);
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c
index faa3844..c0ab868 100644
--- a/drivers/firewire/fw-ohci.c
+++ b/drivers/firewire/fw-ohci.c
@@ -570,13 +570,19 @@
 static void context_stop(struct context *ctx)
 {
 	u32 reg;
+	int i;
 
 	reg_write(ctx->ohci, control_clear(ctx->regs), CONTEXT_RUN);
+	flush_writes(ctx->ohci);
 
-	reg = reg_read(ctx->ohci, control_set(ctx->regs));
-	if (reg & CONTEXT_ACTIVE)
-		fw_notify("Tried to stop context, but it is still active "
-			  "(0x%08x).\n", reg);
+	for (i = 0; i < 10; i++) {
+		reg = reg_read(ctx->ohci, control_set(ctx->regs));
+		if ((reg & CONTEXT_ACTIVE) == 0)
+			break;
+
+		fw_notify("context_stop: still active (0x%08x)\n", reg);
+		msleep(1);
+	}
 }
 
 static void
@@ -1379,6 +1385,25 @@
 	return 0;
 }
 
+static int ohci_stop_iso(struct fw_iso_context *base)
+{
+	struct fw_ohci *ohci = fw_ohci(base->card);
+ 	struct iso_context *ctx = container_of(base, struct iso_context, base);
+	int index;
+
+	if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) {
+		index = ctx - ohci->it_context_list;
+		reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 1 << index);
+	} else {
+		index = ctx - ohci->ir_context_list;
+		reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 1 << index);
+	}
+	flush_writes(ohci);
+	context_stop(&ctx->context);
+
+	return 0;
+}
+
 static void ohci_free_iso_context(struct fw_iso_context *base)
 {
 	struct fw_ohci *ohci = fw_ohci(base->card);
@@ -1386,22 +1411,18 @@
 	unsigned long flags;
 	int index;
 
+	ohci_stop_iso(base);
+	context_release(&ctx->context);
+
 	spin_lock_irqsave(&ohci->lock, flags);
 
 	if (ctx->base.type == FW_ISO_CONTEXT_TRANSMIT) {
 		index = ctx - ohci->it_context_list;
-		reg_write(ohci, OHCI1394_IsoXmitContextControlClear(index), ~0);
-		reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 1 << index);
 		ohci->it_context_mask |= 1 << index;
 	} else {
 		index = ctx - ohci->ir_context_list;
-		reg_write(ohci, OHCI1394_IsoRcvContextControlClear(index), ~0);
-		reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 1 << index);
 		ohci->ir_context_mask |= 1 << index;
 	}
-	flush_writes(ohci);
-
-	context_release(&ctx->context);
 
 	spin_unlock_irqrestore(&ohci->lock, flags);
 }
@@ -1595,6 +1616,7 @@
 	.free_iso_context	= ohci_free_iso_context,
 	.queue_iso		= ohci_queue_iso,
 	.start_iso		= ohci_start_iso,
+	.stop_iso		= ohci_stop_iso,
 };
 
 static int software_reset(struct fw_ohci *ohci)
diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h
index 9ccbed8..b2a0a03 100644
--- a/drivers/firewire/fw-transaction.h
+++ b/drivers/firewire/fw-transaction.h
@@ -386,6 +386,9 @@
 fw_iso_context_start(struct fw_iso_context *ctx,
 		    int channel, int speed, int cycle);
 
+int
+fw_iso_context_stop(struct fw_iso_context *ctx);
+
 struct fw_card_driver {
 	const char *name;
 
@@ -428,6 +431,8 @@
 			 struct fw_iso_packet *packet,
 			 struct fw_iso_buffer *buffer,
 			 unsigned long payload);
+
+	int (*stop_iso)(struct fw_iso_context *ctx);
 };
 
 int