firewire: Add read/write and size annotations to IOC numbers.

Also, with this change, refactor ioctl dispatch code to do the
copying from and to user space as indicated by the IOC annotations.

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 fab6dfb..d2b867f 100644
--- a/drivers/firewire/fw-device-cdev.c
+++ b/drivers/firewire/fw-device-cdev.c
@@ -258,41 +258,35 @@
 	for_each_client(device, wake_up_client);
 }
 
-static int ioctl_get_info(struct client *client, void __user *arg)
+static int ioctl_get_info(struct client *client, void *buffer)
 {
-	struct fw_cdev_get_info get_info;
+	struct fw_cdev_get_info *get_info = buffer;
 	struct fw_cdev_event_bus_reset bus_reset;
 
-	if (copy_from_user(&get_info, arg, sizeof get_info))
-		return -EFAULT;
+	client->version = get_info->version;
+	get_info->version = FW_CDEV_VERSION;
 
-	client->version = get_info.version;
-	get_info.version = FW_CDEV_VERSION;
-
-	if (get_info.rom != 0) {
-		void __user *uptr = u64_to_uptr(get_info.rom);
-		size_t want = get_info.rom_length;
+	if (get_info->rom != 0) {
+		void __user *uptr = u64_to_uptr(get_info->rom);
+		size_t want = get_info->rom_length;
 		size_t have = client->device->config_rom_length * 4;
 
 		if (copy_to_user(uptr, client->device->config_rom,
 				 min(want, have)))
 			return -EFAULT;
 	}
-	get_info.rom_length = client->device->config_rom_length * 4;
+	get_info->rom_length = client->device->config_rom_length * 4;
 
-	client->bus_reset_closure = get_info.bus_reset_closure;
-	if (get_info.bus_reset != 0) {
-		void __user *uptr = u64_to_uptr(get_info.bus_reset);
+	client->bus_reset_closure = get_info->bus_reset_closure;
+	if (get_info->bus_reset != 0) {
+		void __user *uptr = u64_to_uptr(get_info->bus_reset);
 
 		fill_bus_reset_event(&bus_reset, client);
 		if (copy_to_user(uptr, &bus_reset, sizeof bus_reset))
 			return -EFAULT;
 	}
 
-	get_info.card = client->device->card->index;
-
-	if (copy_to_user(arg, &get_info, sizeof get_info))
-		return -EFAULT;
+	get_info->card = client->device->card->index;
 
 	return 0;
 }
@@ -369,30 +363,27 @@
 		    response->response.data, response->response.length);
 }
 
-static ssize_t ioctl_send_request(struct client *client, void __user *arg)
+static ssize_t ioctl_send_request(struct client *client, void *buffer)
 {
 	struct fw_device *device = client->device;
-	struct fw_cdev_send_request request;
+	struct fw_cdev_send_request *request = buffer;
 	struct response *response;
 
-	if (copy_from_user(&request, arg, sizeof request))
-		return -EFAULT;
-
 	/* What is the biggest size we'll accept, really? */
-	if (request.length > 4096)
+	if (request->length > 4096)
 		return -EINVAL;
 
-	response = kmalloc(sizeof *response + request.length, GFP_KERNEL);
+	response = kmalloc(sizeof *response + request->length, GFP_KERNEL);
 	if (response == NULL)
 		return -ENOMEM;
 
 	response->client = client;
-	response->response.length = request.length;
-	response->response.closure = request.closure;
+	response->response.length = request->length;
+	response->response.closure = request->closure;
 
-	if (request.data &&
+	if (request->data &&
 	    copy_from_user(response->response.data,
-			   u64_to_uptr(request.data), request.length)) {
+			   u64_to_uptr(request->data), request->length)) {
 		kfree(response);
 		return -EFAULT;
 	}
@@ -401,16 +392,16 @@
 	add_client_resource(client, &response->resource);
 
 	fw_send_request(device->card, &response->transaction,
-			request.tcode & 0x1f,
+			request->tcode & 0x1f,
 			device->node->node_id,
-			request.generation,
+			request->generation,
 			device->node->max_speed,
-			request.offset,
-			response->response.data, request.length,
+			request->offset,
+			response->response.data, request->length,
 			complete_transaction, response);
 
-	if (request.data)
-		return sizeof request + request.length;
+	if (request->data)
+		return sizeof request + request->length;
 	else
 		return sizeof request;
 }
@@ -495,25 +486,22 @@
 	kfree(handler);
 }
 
-static int ioctl_allocate(struct client *client, void __user *arg)
+static int ioctl_allocate(struct client *client, void *buffer)
 {
-	struct fw_cdev_allocate request;
+	struct fw_cdev_allocate *request = buffer;
 	struct address_handler *handler;
 	struct fw_address_region region;
 
-	if (copy_from_user(&request, arg, sizeof request))
-		return -EFAULT;
-
 	handler = kmalloc(sizeof *handler, GFP_KERNEL);
 	if (handler == NULL)
 		return -ENOMEM;
 
-	region.start = request.offset;
-	region.end = request.offset + request.length;
-	handler->handler.length = request.length;
+	region.start = request->offset;
+	region.end = request->offset + request->length;
+	handler->handler.length = request->length;
 	handler->handler.address_callback = handle_request;
 	handler->handler.callback_data = handler;
-	handler->closure = request.closure;
+	handler->closure = request->closure;
 	handler->client = client;
 
 	if (fw_core_add_address_handler(&handler->handler, &region) < 0) {
@@ -523,55 +511,44 @@
 
 	handler->resource.release = release_address_handler;
 	add_client_resource(client, &handler->resource);
-	request.handle = handler->resource.handle;
-
-	if (copy_to_user(arg, &request, sizeof request))
-		return -EFAULT;
+	request->handle = handler->resource.handle;
 
 	return 0;
 }
 
-static int ioctl_deallocate(struct client *client, void __user *arg)
+static int ioctl_deallocate(struct client *client, void *buffer)
 {
-	struct fw_cdev_deallocate request;
+	struct fw_cdev_deallocate *request = buffer;
 
-	if (copy_from_user(&request, arg, sizeof request))
-		return -EFAULT;
-
-	return release_client_resource(client, request.handle, NULL);
+	return release_client_resource(client, request->handle, NULL);
 }
 
-static int ioctl_send_response(struct client *client, void __user *arg)
+static int ioctl_send_response(struct client *client, void *buffer)
 {
-	struct fw_cdev_send_response request;
+	struct fw_cdev_send_response *request = buffer;
 	struct client_resource *resource;
 	struct request *r;
 
-	if (copy_from_user(&request, arg, sizeof request))
-		return -EFAULT;
-	if (release_client_resource(client, request.handle, &resource) < 0)
+	if (release_client_resource(client, request->handle, &resource) < 0)
 		return -EINVAL;
 	r = container_of(resource, struct request, resource);
-	if (request.length < r->length)
-		r->length = request.length;
-	if (copy_from_user(r->data, u64_to_uptr(request.data), r->length))
+	if (request->length < r->length)
+		r->length = request->length;
+	if (copy_from_user(r->data, u64_to_uptr(request->data), r->length))
 		return -EFAULT;
 
-	fw_send_response(client->device->card, r->request, request.rcode);
+	fw_send_response(client->device->card, r->request, request->rcode);
 	kfree(r);
 
 	return 0;
 }
 
-static int ioctl_initiate_bus_reset(struct client *client, void __user *arg)
+static int ioctl_initiate_bus_reset(struct client *client, void *buffer)
 {
-	struct fw_cdev_initiate_bus_reset request;
+	struct fw_cdev_initiate_bus_reset *request = buffer;
 	int short_reset;
 
-	if (copy_from_user(&request, arg, sizeof request))
-		return -EFAULT;
-
-	short_reset = (request.type == FW_CDEV_SHORT_RESET);
+	short_reset = (request->type == FW_CDEV_SHORT_RESET);
 
 	return fw_core_initiate_bus_reset(client->device->card, short_reset);
 }
@@ -592,32 +569,29 @@
 	kfree(descriptor);
 }
 
-static int ioctl_add_descriptor(struct client *client, void __user *arg)
+static int ioctl_add_descriptor(struct client *client, void *buffer)
 {
-	struct fw_cdev_add_descriptor request;
+	struct fw_cdev_add_descriptor *request = buffer;
 	struct descriptor *descriptor;
 	int retval;
 
-	if (copy_from_user(&request, arg, sizeof request))
-		return -EFAULT;
-
-	if (request.length > 256)
+	if (request->length > 256)
 		return -EINVAL;
 
 	descriptor =
-		kmalloc(sizeof *descriptor + request.length * 4, GFP_KERNEL);
+		kmalloc(sizeof *descriptor + request->length * 4, GFP_KERNEL);
 	if (descriptor == NULL)
 		return -ENOMEM;
 
 	if (copy_from_user(descriptor->data,
-			   u64_to_uptr(request.data), request.length * 4)) {
+			   u64_to_uptr(request->data), request->length * 4)) {
 		kfree(descriptor);
 		return -EFAULT;
 	}
 
-	descriptor->d.length = request.length;
-	descriptor->d.immediate = request.immediate;
-	descriptor->d.key = request.key;
+	descriptor->d.length = request->length;
+	descriptor->d.immediate = request->immediate;
+	descriptor->d.key = request->key;
 	descriptor->d.data = descriptor->data;
 
 	retval = fw_core_add_descriptor(&descriptor->d);
@@ -628,22 +602,16 @@
 
 	descriptor->resource.release = release_descriptor;
 	add_client_resource(client, &descriptor->resource);
-	request.handle = descriptor->resource.handle;
-
-	if (copy_to_user(arg, &request, sizeof request))
-		return -EFAULT;
+	request->handle = descriptor->resource.handle;
 
 	return 0;
 }
 
-static int ioctl_remove_descriptor(struct client *client, void __user *arg)
+static int ioctl_remove_descriptor(struct client *client, void *buffer)
 {
-	struct fw_cdev_remove_descriptor request;
+	struct fw_cdev_remove_descriptor *request = buffer;
 
-	if (copy_from_user(&request, arg, sizeof request))
-		return -EFAULT;
-
-	return release_client_resource(client, request.handle, NULL);
+	return release_client_resource(client, request->handle, NULL);
 }
 
 static void
@@ -667,25 +635,22 @@
 		    sizeof interrupt->interrupt + header_length, NULL, 0);
 }
 
-static int ioctl_create_iso_context(struct client *client, void __user *arg)
+static int ioctl_create_iso_context(struct client *client, void *buffer)
 {
-	struct fw_cdev_create_iso_context request;
+	struct fw_cdev_create_iso_context *request = buffer;
 
-	if (copy_from_user(&request, arg, sizeof request))
-		return -EFAULT;
-
-	if (request.channel > 63)
+	if (request->channel > 63)
 		return -EINVAL;
 
-	switch (request.type) {
+	switch (request->type) {
 	case FW_ISO_CONTEXT_RECEIVE:
-		if (request.header_size < 4 || (request.header_size & 3))
+		if (request->header_size < 4 || (request->header_size & 3))
 			return -EINVAL;
 
 		break;
 
 	case FW_ISO_CONTEXT_TRANSMIT:
-		if (request.speed > SCODE_3200)
+		if (request->speed > SCODE_3200)
 			return -EINVAL;
 
 		break;
@@ -695,10 +660,10 @@
 	}
 
 	client->iso_context = fw_iso_context_create(client->device->card,
-						    request.type,
-						    request.channel,
-						    request.speed,
-						    request.header_size,
+						    request->type,
+						    request->channel,
+						    request->speed,
+						    request->header_size,
 						    iso_callback, client);
 	if (IS_ERR(client->iso_context))
 		return PTR_ERR(client->iso_context);
@@ -706,9 +671,9 @@
 	return 0;
 }
 
-static int ioctl_queue_iso(struct client *client, void __user *arg)
+static int ioctl_queue_iso(struct client *client, void *buffer)
 {
-	struct fw_cdev_queue_iso request;
+	struct fw_cdev_queue_iso *request = buffer;
 	struct fw_cdev_iso_packet __user *p, *end, *next;
 	struct fw_iso_context *ctx = client->iso_context;
 	unsigned long payload, buffer_end, header_length;
@@ -720,8 +685,6 @@
 
 	if (ctx == NULL)
 		return -EINVAL;
-	if (copy_from_user(&request, arg, sizeof request))
-		return -EFAULT;
 
 	/* If the user passes a non-NULL data pointer, has mmap()'ed
 	 * the iso buffer, and the pointer points inside the buffer,
@@ -729,21 +692,21 @@
 	 * set them both to 0, which will still let packets with
 	 * payload_length == 0 through.  In other words, if no packets
 	 * use the indirect payload, the iso buffer need not be mapped
-	 * and the request.data pointer is ignored.*/
+	 * and the request->data pointer is ignored.*/
 
-	payload = (unsigned long)request.data - client->vm_start;
+	payload = (unsigned long)request->data - client->vm_start;
 	buffer_end = client->buffer.page_count << PAGE_SHIFT;
-	if (request.data == 0 || client->buffer.pages == NULL ||
+	if (request->data == 0 || client->buffer.pages == NULL ||
 	    payload >= buffer_end) {
 		payload = 0;
 		buffer_end = 0;
 	}
 
-	if (!access_ok(VERIFY_READ, request.packets, request.size))
+	if (!access_ok(VERIFY_READ, request->packets, request->size))
 		return -EFAULT;
 
-	p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request.packets);
-	end = (void __user *)p + request.size;
+	p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets);
+	end = (void __user *)p + request->size;
 	count = 0;
 	while (p < end) {
 		if (__copy_from_user(&u.packet, p, sizeof *p))
@@ -785,71 +748,76 @@
 		count++;
 	}
 
-	request.size    -= uptr_to_u64(p) - request.packets;
-	request.packets  = uptr_to_u64(p);
-	request.data     = client->vm_start + payload;
-
-	if (copy_to_user(arg, &request, sizeof request))
-		return -EFAULT;
+	request->size    -= uptr_to_u64(p) - request->packets;
+	request->packets  = uptr_to_u64(p);
+	request->data     = client->vm_start + payload;
 
 	return count;
 }
 
-static int ioctl_start_iso(struct client *client, void __user *arg)
+static int ioctl_start_iso(struct client *client, void *buffer)
 {
-	struct fw_cdev_start_iso request;
-
-	if (copy_from_user(&request, arg, sizeof request))
-		return -EFAULT;
+	struct fw_cdev_start_iso *request = buffer;
 
 	if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) {
-		if (request.tags == 0 || request.tags > 15)
+		if (request->tags == 0 || request->tags > 15)
 			return -EINVAL;
 
-		if (request.sync > 15)
+		if (request->sync > 15)
 			return -EINVAL;
 	}
 
-	return fw_iso_context_start(client->iso_context,
-				    request.cycle, request.sync, request.tags);
+	return fw_iso_context_start(client->iso_context, request->cycle,
+				    request->sync, request->tags);
 }
 
-static int ioctl_stop_iso(struct client *client, void __user *arg)
+static int ioctl_stop_iso(struct client *client, void *buffer)
 {
 	return fw_iso_context_stop(client->iso_context);
 }
 
+static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
+	ioctl_get_info,
+	ioctl_send_request,
+	ioctl_allocate,
+	ioctl_deallocate,
+	ioctl_send_response,
+	ioctl_initiate_bus_reset,
+	ioctl_add_descriptor,
+	ioctl_remove_descriptor,
+	ioctl_create_iso_context,
+	ioctl_queue_iso,
+	ioctl_start_iso,
+	ioctl_stop_iso,
+};
+
 static int
 dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
 {
-	switch (cmd) {
-	case FW_CDEV_IOC_GET_INFO:
-		return ioctl_get_info(client, arg);
-	case FW_CDEV_IOC_SEND_REQUEST:
-		return ioctl_send_request(client, arg);
-	case FW_CDEV_IOC_ALLOCATE:
-		return ioctl_allocate(client, arg);
-	case FW_CDEV_IOC_DEALLOCATE:
-		return ioctl_deallocate(client, arg);
-	case FW_CDEV_IOC_SEND_RESPONSE:
-		return ioctl_send_response(client, arg);
-	case FW_CDEV_IOC_INITIATE_BUS_RESET:
-		return ioctl_initiate_bus_reset(client, arg);
-	case FW_CDEV_IOC_ADD_DESCRIPTOR:
-		return ioctl_add_descriptor(client, arg);
-	case FW_CDEV_IOC_REMOVE_DESCRIPTOR:
-		return ioctl_remove_descriptor(client, arg);
-	case FW_CDEV_IOC_CREATE_ISO_CONTEXT:
-		return ioctl_create_iso_context(client, arg);
-	case FW_CDEV_IOC_QUEUE_ISO:
-		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:
+	char buffer[256];
+	int retval;
+
+	if (_IOC_TYPE(cmd) != '#' ||
+	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers))
 		return -EINVAL;
+
+	if (_IOC_DIR(cmd) & _IOC_WRITE) {
+		if (_IOC_SIZE(cmd) > sizeof buffer ||
+		    copy_from_user(buffer, arg, _IOC_SIZE(cmd)))
+			return -EFAULT;
 	}
+
+	retval = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
+	if (retval < 0)
+		return retval;
+
+	if (_IOC_DIR(cmd) & _IOC_READ) {
+		if (_IOC_SIZE(cmd) > sizeof buffer ||
+		    copy_to_user(arg, buffer, _IOC_SIZE(cmd)))
+			return -EFAULT;
+	}
+
+	return 0;
 }
 
 static long