firewire: cdev: add ioctl for broadcast write requests

Write transactions to the broadcast node ID are a convenient way to
trigger functions of multiple nodes at once.  IIDC is a protocol which
can make use of this if multiple cameras with same command_regs_base are
connected at the same bus.

Based on
    Date: Wed, 10 Sep 2008 11:32:16 -0400
    From: Jay Fenlason <fenlason@redhat.com>
    Subject: [patch] SEND_BROADCAST_REQUEST
Changes:  ioctl_send_request() and ioctl_send_broadcast_request() now
share code.  Broadcast speed corrected to S100.  Check for proper tcode.

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
diff --git a/drivers/firewire/fw-cdev.c b/drivers/firewire/fw-cdev.c
index 05ad2a8..a1637a8 100644
--- a/drivers/firewire/fw-cdev.c
+++ b/drivers/firewire/fw-cdev.c
@@ -518,10 +518,10 @@
 	client_put(client);
 }
 
-static int ioctl_send_request(struct client *client, void *buffer)
+static int init_request(struct client *client,
+			struct fw_cdev_send_request *request,
+			int destination_id, int speed)
 {
-	struct fw_device *device = client->device;
-	struct fw_cdev_send_request *request = buffer;
 	struct outbound_transaction_event *e;
 	int ret;
 
@@ -544,6 +544,34 @@
 		goto failed;
 	}
 
+	e->r.resource.release = release_transaction;
+	ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
+	if (ret < 0)
+		goto failed;
+
+	/* Get a reference for the transaction callback */
+	client_get(client);
+
+	fw_send_request(client->device->card, &e->r.transaction,
+			request->tcode & 0x1f, destination_id,
+			request->generation, speed, request->offset,
+			e->response.data, request->length,
+			complete_transaction, e);
+
+	if (request->data)
+		return sizeof(request) + request->length;
+	else
+		return sizeof(request);
+ failed:
+	kfree(e);
+
+	return ret;
+}
+
+static int ioctl_send_request(struct client *client, void *buffer)
+{
+	struct fw_cdev_send_request *request = buffer;
+
 	switch (request->tcode) {
 	case TCODE_WRITE_QUADLET_REQUEST:
 	case TCODE_WRITE_BLOCK_REQUEST:
@@ -558,35 +586,11 @@
 	case TCODE_LOCK_VENDOR_DEPENDENT:
 		break;
 	default:
-		ret = -EINVAL;
-		goto failed;
+		return -EINVAL;
 	}
 
-	e->r.resource.release = release_transaction;
-	ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
-	if (ret < 0)
-		goto failed;
-
-	/* Get a reference for the transaction callback */
-	client_get(client);
-
-	fw_send_request(device->card, &e->r.transaction,
-			request->tcode & 0x1f,
-			device->node->node_id,
-			request->generation,
-			device->max_speed,
-			request->offset,
-			e->response.data, request->length,
-			complete_transaction, e);
-
-	if (request->data)
-		return sizeof(request) + request->length;
-	else
-		return sizeof(request);
- failed:
-	kfree(e);
-
-	return ret;
+	return init_request(client, request, client->device->node->node_id,
+			    client->device->max_speed);
 }
 
 static void release_request(struct client *client,
@@ -1229,6 +1233,21 @@
 	return 0;
 }
 
+static int ioctl_send_broadcast_request(struct client *client, void *buffer)
+{
+	struct fw_cdev_send_request *request = buffer;
+
+	switch (request->tcode) {
+	case TCODE_WRITE_QUADLET_REQUEST:
+	case TCODE_WRITE_BLOCK_REQUEST:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100);
+}
+
 static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
 	ioctl_get_info,
 	ioctl_send_request,
@@ -1248,6 +1267,7 @@
 	ioctl_allocate_iso_resource_once,
 	ioctl_deallocate_iso_resource_once,
 	ioctl_get_speed,
+	ioctl_send_broadcast_request,
 };
 
 static int dispatch_ioctl(struct client *client,