core-cdev.c revision d67cfb9613f373d76daa2c8d209629601424ca12
1c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg/*
2c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * Char device for device raw access
319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg *
4c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * Copyright (C) 2005-2007  Kristian Hoegsberg <krh@bitplanet.net>
519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg *
619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * This program is free software; you can redistribute it and/or modify
719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * it under the terms of the GNU General Public License as published by
819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * the Free Software Foundation; either version 2 of the License, or
919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * (at your option) any later version.
1019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg *
1119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * This program is distributed in the hope that it will be useful,
1219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * but WITHOUT ANY WARRANTY; without even the implied warranty of
1319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * GNU General Public License for more details.
1519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg *
1619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * You should have received a copy of the GNU General Public License
1719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * along with this program; if not, write to the Free Software Foundation,
1819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
1919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg */
2019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/module.h>
2219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/kernel.h>
2319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/wait.h>
2419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/errno.h>
2519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/device.h>
2619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/vmalloc.h>
27d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter#include <linux/mutex.h>
2819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/poll.h>
29a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter#include <linux/preempt.h>
30a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter#include <linux/time.h>
31cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason#include <linux/spinlock.h>
3219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/delay.h>
3319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/mm.h>
34a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg#include <linux/idr.h>
3519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/compat.h>
369640d3d775aa325650c8fcdf49127542f77b2156Kristian Høgsberg#include <linux/firewire-cdev.h>
37a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter#include <asm/system.h>
3819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <asm/uaccess.h>
3919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include "fw-transaction.h"
4019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include "fw-topology.h"
4119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include "fw-device.h"
4219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
433964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstruct client;
443964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstruct client_resource {
453964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct list_head link;
463964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	void (*release)(struct client *client, struct client_resource *r);
473964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	u32 handle;
483964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg};
493964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
50c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg/*
51c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * dequeue_event() just kfree()'s the event, so the event has to be
52c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * the first field in the struct.
53c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg */
54c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg
5519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct event {
5619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct { void *data; size_t size; } v[2];
5719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct list_head link;
5819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
5919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergstruct bus_reset {
6197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct event event;
6297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct fw_cdev_event_bus_reset reset;
6397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg};
6497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
6519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct response {
6619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event event;
6719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_transaction transaction;
6819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client;
693964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource resource;
7019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_event_response response;
7119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
7219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct iso_interrupt {
7419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event event;
7519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_event_iso_interrupt interrupt;
7619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
7719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct client {
79344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	u32 version;
8019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device;
8119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spinlock_t lock;
8266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	u32 resource_handle;
833964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct list_head resource_list;
8419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct list_head event_list;
8519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	wait_queue_head_t wait;
86da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	u64 bus_reset_closure;
879aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
8819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_iso_context *iso_context;
89abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	u64 iso_closure;
909aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	struct fw_iso_buffer buffer;
919aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	unsigned long vm_start;
9297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
9397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct list_head link;
9419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
9519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic inline void __user *
9719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergu64_to_uptr(__u64 value)
9819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
9919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return (void __user *)(unsigned long)value;
10019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
10119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic inline __u64
10319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberguptr_to_u64(void __user *ptr)
10419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
10519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return (__u64)(unsigned long)ptr;
10619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
10719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_open(struct inode *inode, struct file *file)
10919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
11019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device;
11119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client;
11219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
11396b19062e741b715cf399312c30e0672d8889569Stefan Richter	device = fw_device_get_by_devt(inode->i_rdev);
114a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg	if (device == NULL)
115a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg		return -ENODEV;
11619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
117551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(device)) {
118551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		fw_device_put(device);
119551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
120551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	}
121551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
1222d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	client = kzalloc(sizeof(*client), GFP_KERNEL);
12396b19062e741b715cf399312c30e0672d8889569Stefan Richter	if (client == NULL) {
12496b19062e741b715cf399312c30e0672d8889569Stefan Richter		fw_device_put(device);
12519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
12696b19062e741b715cf399312c30e0672d8889569Stefan Richter	}
12719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
12896b19062e741b715cf399312c30e0672d8889569Stefan Richter	client->device = device;
12919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	INIT_LIST_HEAD(&client->event_list);
1303964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	INIT_LIST_HEAD(&client->resource_list);
13119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_lock_init(&client->lock);
13219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	init_waitqueue_head(&client->wait);
13319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
13419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	file->private_data = client;
13519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
136d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_lock(&device->client_list_mutex);
13797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	list_add_tail(&client->link, &device->client_list);
138d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_unlock(&device->client_list_mutex);
13997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
14019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
14119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
14219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
14319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void queue_event(struct client *client, struct event *event,
14419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			void *data0, size_t size0, void *data1, size_t size1)
14519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
14619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	unsigned long flags;
14719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
14819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[0].data = data0;
14919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[0].size = size0;
15019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[1].data = data1;
15119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[1].size = size1;
15219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
15319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
15419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	list_add_tail(&event->link, &client->event_list);
15519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
15683431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason
15783431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason	wake_up_interruptible(&client->wait);
15819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
15919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1602603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic int
1612603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergdequeue_event(struct client *client, char __user *buffer, size_t count)
16219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
16319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	unsigned long flags;
16419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event *event;
16519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	size_t size, total;
1662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	int i, retval;
16719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1682603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	retval = wait_event_interruptible(client->wait,
1692603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg					  !list_empty(&client->event_list) ||
1702603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg					  fw_device_is_shutdown(client->device));
1712603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (retval < 0)
1722603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		return retval;
17319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1742603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (list_empty(&client->event_list) &&
1752603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		       fw_device_is_shutdown(client->device))
1762603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		return -ENODEV;
17719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1782603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
17919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event = container_of(client->event_list.next, struct event, link);
18019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	list_del(&event->link);
18119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
18219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
18319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	total = 0;
18419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
18519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		size = min(event->v[i].size, count - total);
1862603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		if (copy_to_user(buffer + total, event->v[i].data, size)) {
1872603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg			retval = -EFAULT;
18819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			goto out;
1892603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		}
19019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		total += size;
19119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
19219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	retval = total;
19319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
19419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg out:
19519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(event);
19619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
19719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return retval;
19819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
19919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
20019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic ssize_t
20119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergfw_device_op_read(struct file *file,
20219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		  char __user *buffer, size_t count, loff_t *offset)
20319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
20419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
20519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
20619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dequeue_event(client, buffer, count);
20719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
20819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
20997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergstatic void
210344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsbergfill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
211da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg		     struct client *client)
212344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg{
213da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	struct fw_card *card = client->device->card;
214cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason	unsigned long flags;
215cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason
216cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason	spin_lock_irqsave(&card->lock, flags);
217344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
218da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	event->closure	     = client->bus_reset_closure;
219344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->type          = FW_CDEV_EVENT_BUS_RESET;
220cf5a56ac8083dd04ffe8b9b2ec7895e9bcff44bcStefan Richter	event->generation    = client->device->generation;
221da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	event->node_id       = client->device->node_id;
222344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->local_node_id = card->local_node->node_id;
223344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->bm_node_id    = 0; /* FIXME: We don't track the BM. */
224344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->irm_node_id   = card->irm_node->node_id;
225344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->root_node_id  = card->root_node->node_id;
226cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason
227cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason	spin_unlock_irqrestore(&card->lock, flags);
228344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg}
229344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
230344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsbergstatic void
2312603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergfor_each_client(struct fw_device *device,
2322603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		void (*callback)(struct client *client))
2332603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
2342603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	struct client *c;
2352603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
236d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_lock(&device->client_list_mutex);
2372603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	list_for_each_entry(c, &device->client_list, link)
2382603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		callback(c);
239d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_unlock(&device->client_list_mutex);
2402603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
2412603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
2422603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void
24397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergqueue_bus_reset_event(struct client *client)
24497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{
24597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct bus_reset *bus_reset;
24697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
247d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	bus_reset = kzalloc(sizeof(*bus_reset), GFP_KERNEL);
24897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	if (bus_reset == NULL) {
24997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg		fw_notify("Out of memory when allocating bus reset event\n");
25097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg		return;
25197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	}
25297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
253da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	fill_bus_reset_event(&bus_reset->reset, client);
25497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
25597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	queue_event(client, &bus_reset->event,
2562d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		    &bus_reset->reset, sizeof(bus_reset->reset), NULL, 0);
25797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg}
25897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
25997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergvoid fw_device_cdev_update(struct fw_device *device)
26097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{
2612603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	for_each_client(device, queue_bus_reset_event);
2622603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
26397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
2642603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void wake_up_client(struct client *client)
2652603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
2662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	wake_up_interruptible(&client->wait);
2672603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
26897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
2692603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergvoid fw_device_cdev_remove(struct fw_device *device)
2702603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
2712603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	for_each_client(device, wake_up_client);
27297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg}
27397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
2744f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_get_info(struct client *client, void *buffer)
27519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
2764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_get_info *get_info = buffer;
277344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	struct fw_cdev_event_bus_reset bus_reset;
278c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	unsigned long ret = 0;
279344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
2804f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	client->version = get_info->version;
2814f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	get_info->version = FW_CDEV_VERSION;
282cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason	get_info->card = client->device->card->index;
283344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
284c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	down_read(&fw_device_rwsem);
285c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
2864f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (get_info->rom != 0) {
2874f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		void __user *uptr = u64_to_uptr(get_info->rom);
2884f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		size_t want = get_info->rom_length;
289d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter		size_t have = client->device->config_rom_length * 4;
290344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
291c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter		ret = copy_to_user(uptr, client->device->config_rom,
292c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter				   min(want, have));
293344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	}
2944f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	get_info->rom_length = client->device->config_rom_length * 4;
295344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
296c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	up_read(&fw_device_rwsem);
297c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
298c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	if (ret != 0)
299c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter		return -EFAULT;
300c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
3014f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	client->bus_reset_closure = get_info->bus_reset_closure;
3024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (get_info->bus_reset != 0) {
3034f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		void __user *uptr = u64_to_uptr(get_info->bus_reset);
304344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
305da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg		fill_bus_reset_event(&bus_reset, client);
3062d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset)))
307344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg			return -EFAULT;
308344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	}
30919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
31019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
31119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
31219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
31319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void
3143964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergadd_client_resource(struct client *client, struct client_resource *resource)
3153964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
3163964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	unsigned long flags;
3173964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3183964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
3193964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	list_add_tail(&resource->link, &client->resource_list);
3203964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	resource->handle = client->resource_handle++;
3213964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
3223964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
3233964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3243964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic int
3253964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergrelease_client_resource(struct client *client, u32 handle,
3263964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			struct client_resource **resource)
3273964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
3283964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource *r;
3293964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	unsigned long flags;
3303964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3313964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
3323964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	list_for_each_entry(r, &client->resource_list, link) {
3333964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		if (r->handle == handle) {
3343964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			list_del(&r->link);
3353964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			break;
3363964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		}
3373964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	}
3383964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
3393964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3403964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	if (&r->link == &client->resource_list)
3413964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		return -EINVAL;
3423964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3433964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	if (resource)
3443964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		*resource = r;
3453964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	else
3463964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		r->release(client, r);
3473964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3483964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	return 0;
3493964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
3503964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3513964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void
3523964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergrelease_transaction(struct client *client, struct client_resource *resource)
3533964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
3543964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct response *response =
3553964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		container_of(resource, struct response, resource);
3563964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3573964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	fw_cancel_transaction(client->device->card, &response->transaction);
3583964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
3593964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3603964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void
36119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergcomplete_transaction(struct fw_card *card, int rcode,
36219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		     void *payload, size_t length, void *data)
36319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
36419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct response *response = data;
36519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = response->client;
36628cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	unsigned long flags;
3678401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	struct fw_cdev_event_response *r = &response->response;
36819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
3698401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	if (length < r->length)
3708401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore		r->length = length;
37119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (rcode == RCODE_COMPLETE)
3728401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore		memcpy(r->data, payload, r->length);
37319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
37428cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
3753964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	list_del(&response->resource.link);
37628cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
37728cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
3788401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	r->type   = FW_CDEV_EVENT_RESPONSE;
3798401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	r->rcode  = rcode;
3808401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore
3818401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	/*
3828401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * In the case that sizeof(*r) doesn't align with the position of the
3838401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * data, and the read is short, preserve an extra copy of the data
3848401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * to stay compatible with a pre-2.6.27 bug.  Since the bug is harmless
3858401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * for short reads and some apps depended on it, this is both safe
3868401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * and prudent for compatibility.
3878401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 */
3888401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	if (r->length <= sizeof(*r) - offsetof(typeof(*r), data))
3898401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore		queue_event(client, &response->event, r, sizeof(*r),
3908401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore			    r->data, r->length);
3918401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	else
3928401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore		queue_event(client, &response->event, r, sizeof(*r) + r->length,
3938401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore			    NULL, 0);
39419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
39519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
396350958f984268dcf0f087aac78c5b9fe2846aeffJeff Garzikstatic int ioctl_send_request(struct client *client, void *buffer)
39719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
39819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device = client->device;
3994f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_send_request *request = buffer;
40019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct response *response;
40119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
40219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	/* What is the biggest size we'll accept, really? */
4034f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->length > 4096)
40419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
40519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4062d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	response = kmalloc(sizeof(*response) + request->length, GFP_KERNEL);
40719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (response == NULL)
40819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
40919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
41019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	response->client = client;
4114f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	response->response.length = request->length;
4124f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	response->response.closure = request->closure;
41319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4144f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->data &&
41519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	    copy_from_user(response->response.data,
4164f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			   u64_to_uptr(request->data), request->length)) {
41719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		kfree(response);
41819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EFAULT;
41919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
42019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4213964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	response->resource.release = release_transaction;
4223964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	add_client_resource(client, &response->resource);
42328cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
42419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	fw_send_request(device->card, &response->transaction,
4254f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			request->tcode & 0x1f,
426907293d78872ee492ce6a114258dd853ec5082aeStefan Richter			device->node->node_id,
4274f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			request->generation,
428f1397490017e337446c6a8b0562b584679a604a6Stefan Richter			device->max_speed,
4294f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			request->offset,
4304f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			response->response.data, request->length,
43119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			complete_transaction, response);
43219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4334f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->data)
4342d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		return sizeof(request) + request->length;
43519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	else
4362d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		return sizeof(request);
43719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
43819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
43919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct address_handler {
44019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_address_handler handler;
44119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	__u64 closure;
44219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client;
4433964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource resource;
44419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
44519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
44619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct request {
44719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_request *request;
44819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	void *data;
44919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	size_t length;
4503964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource resource;
45119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
45219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
45319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct request_event {
45419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event event;
45519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_event_request request;
45619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
45719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
45819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void
4593964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergrelease_request(struct client *client, struct client_resource *resource)
4603964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
4613964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct request *request =
4623964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		container_of(resource, struct request, resource);
4633964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
4643964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	fw_send_response(client->device->card, request->request,
4653964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			 RCODE_CONFLICT_ERROR);
4663964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	kfree(request);
4673964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
4683964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
4693964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void
47019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberghandle_request(struct fw_card *card, struct fw_request *r,
47119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	       int tcode, int destination, int source,
47219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	       int generation, int speed,
47319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	       unsigned long long offset,
47419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	       void *payload, size_t length, void *callback_data)
47519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
47619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct address_handler *handler = callback_data;
47719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct request *request;
47819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct request_event *e;
47919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = handler->client;
48019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4812d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	request = kmalloc(sizeof(*request), GFP_ATOMIC);
4822d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	e = kmalloc(sizeof(*e), GFP_ATOMIC);
48319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (request == NULL || e == NULL) {
48419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		kfree(request);
48519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		kfree(e);
48619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		fw_send_response(card, r, RCODE_CONFLICT_ERROR);
48719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return;
48819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
48919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
49019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	request->request = r;
49119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	request->data    = payload;
49219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	request->length  = length;
49319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4943964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	request->resource.release = release_request;
4953964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	add_client_resource(client, &request->resource);
49619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
49719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.type    = FW_CDEV_EVENT_REQUEST;
49819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.tcode   = tcode;
49919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.offset  = offset;
50019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.length  = length;
5013964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	e->request.handle  = request->resource.handle;
50219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.closure = handler->closure;
50319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
50419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	queue_event(client, &e->event,
5052d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		    &e->request, sizeof(e->request), payload, length);
50619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
50719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5083964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void
5093964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergrelease_address_handler(struct client *client,
5103964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			struct client_resource *resource)
5113964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
5123964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct address_handler *handler =
5133964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		container_of(resource, struct address_handler, resource);
5143964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
5153964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	fw_core_remove_address_handler(&handler->handler);
5163964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	kfree(handler);
5173964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
5183964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
5194f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_allocate(struct client *client, void *buffer)
52019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
5214f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_allocate *request = buffer;
52219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct address_handler *handler;
52319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_address_region region;
52419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5252d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	handler = kmalloc(sizeof(*handler), GFP_KERNEL);
52619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (handler == NULL)
52719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
52819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5294f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	region.start = request->offset;
5304f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	region.end = request->offset + request->length;
5314f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	handler->handler.length = request->length;
53219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	handler->handler.address_callback = handle_request;
53319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	handler->handler.callback_data = handler;
5344f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	handler->closure = request->closure;
53519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	handler->client = client;
53619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
53719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (fw_core_add_address_handler(&handler->handler, &region) < 0) {
53819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		kfree(handler);
53919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EBUSY;
54019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
54119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5423964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	handler->resource.release = release_address_handler;
5433964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	add_client_resource(client, &handler->resource);
5444f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->handle = handler->resource.handle;
54519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
54619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
54719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
54819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5494f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_deallocate(struct client *client, void *buffer)
5509472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg{
5514f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_deallocate *request = buffer;
5529472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg
5534f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	return release_client_resource(client, request->handle, NULL);
5549472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg}
5559472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg
5564f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_send_response(struct client *client, void *buffer)
55719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
5584f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_send_response *request = buffer;
5593964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource *resource;
56019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct request *r;
56119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5624f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (release_client_resource(client, request->handle, &resource) < 0)
56319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
5643964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	r = container_of(resource, struct request, resource);
5654f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->length < r->length)
5664f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		r->length = request->length;
5674f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (copy_from_user(r->data, u64_to_uptr(request->data), r->length))
56819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EFAULT;
56919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5704f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	fw_send_response(client->device->card, r->request, request->rcode);
57119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(r);
57219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
57319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
57419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
57519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_initiate_bus_reset(struct client *client, void *buffer)
5775371842b723dd04df57171f2c74660966901380cKristian Høgsberg{
5784f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_initiate_bus_reset *request = buffer;
5795371842b723dd04df57171f2c74660966901380cKristian Høgsberg	int short_reset;
5805371842b723dd04df57171f2c74660966901380cKristian Høgsberg
5814f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	short_reset = (request->type == FW_CDEV_SHORT_RESET);
5825371842b723dd04df57171f2c74660966901380cKristian Høgsberg
5835371842b723dd04df57171f2c74660966901380cKristian Høgsberg	return fw_core_initiate_bus_reset(client->device->card, short_reset);
5845371842b723dd04df57171f2c74660966901380cKristian Høgsberg}
5855371842b723dd04df57171f2c74660966901380cKristian Høgsberg
58666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsbergstruct descriptor {
58766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	struct fw_descriptor d;
5883964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource resource;
58966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	u32 data[0];
59066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg};
59166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
5923964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void release_descriptor(struct client *client,
5933964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			       struct client_resource *resource)
5943964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
5953964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct descriptor *descriptor =
5963964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		container_of(resource, struct descriptor, resource);
5973964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
5983964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	fw_core_remove_descriptor(&descriptor->d);
5993964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	kfree(descriptor);
6003964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
6013964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
6024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_add_descriptor(struct client *client, void *buffer)
60366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{
6044f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_add_descriptor *request = buffer;
60566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	struct descriptor *descriptor;
60666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	int retval;
60766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
6084f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->length > 256)
60966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -EINVAL;
61066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
61166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	descriptor =
6122d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		kmalloc(sizeof(*descriptor) + request->length * 4, GFP_KERNEL);
61366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	if (descriptor == NULL)
61466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -ENOMEM;
61566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
61666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	if (copy_from_user(descriptor->data,
6174f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			   u64_to_uptr(request->data), request->length * 4)) {
61866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		kfree(descriptor);
61966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -EFAULT;
62066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	}
62166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
6224f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	descriptor->d.length = request->length;
6234f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	descriptor->d.immediate = request->immediate;
6244f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	descriptor->d.key = request->key;
62566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	descriptor->d.data = descriptor->data;
62666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
62766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	retval = fw_core_add_descriptor(&descriptor->d);
62866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	if (retval < 0) {
62966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		kfree(descriptor);
63066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return retval;
63166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	}
63266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
6333964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	descriptor->resource.release = release_descriptor;
6343964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	add_client_resource(client, &descriptor->resource);
6354f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->handle = descriptor->resource.handle;
63666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
63766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	return 0;
63866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg}
63966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
6404f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_remove_descriptor(struct client *client, void *buffer)
64166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{
6424f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_remove_descriptor *request = buffer;
64366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
6444f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	return release_client_resource(client, request->handle, NULL);
64566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg}
64666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
64719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void
6489b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsbergiso_callback(struct fw_iso_context *context, u32 cycle,
6499b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg	     size_t header_length, void *header, void *data)
65019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
65119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = data;
652930e4b7fe3b4e1c4e14b9c22e4c9c74dc8db75f3Stefan Richter	struct iso_interrupt *irq;
65319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
654930e4b7fe3b4e1c4e14b9c22e4c9c74dc8db75f3Stefan Richter	irq = kzalloc(sizeof(*irq) + header_length, GFP_ATOMIC);
655930e4b7fe3b4e1c4e14b9c22e4c9c74dc8db75f3Stefan Richter	if (irq == NULL)
65619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return;
65719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
658930e4b7fe3b4e1c4e14b9c22e4c9c74dc8db75f3Stefan Richter	irq->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
659930e4b7fe3b4e1c4e14b9c22e4c9c74dc8db75f3Stefan Richter	irq->interrupt.closure   = client->iso_closure;
660930e4b7fe3b4e1c4e14b9c22e4c9c74dc8db75f3Stefan Richter	irq->interrupt.cycle     = cycle;
661930e4b7fe3b4e1c4e14b9c22e4c9c74dc8db75f3Stefan Richter	irq->interrupt.header_length = header_length;
662930e4b7fe3b4e1c4e14b9c22e4c9c74dc8db75f3Stefan Richter	memcpy(irq->interrupt.header, header, header_length);
663930e4b7fe3b4e1c4e14b9c22e4c9c74dc8db75f3Stefan Richter	queue_event(client, &irq->event, &irq->interrupt,
664930e4b7fe3b4e1c4e14b9c22e4c9c74dc8db75f3Stefan Richter		    sizeof(irq->interrupt) + header_length, NULL, 0);
66519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
66619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6674f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_create_iso_context(struct client *client, void *buffer)
66819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
6694f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_create_iso_context *request = buffer;
67024315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	struct fw_iso_context *context;
67119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
672fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter	/* We only support one context at this time. */
673fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter	if (client->iso_context != NULL)
674fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter		return -EBUSY;
675fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter
6764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->channel > 63)
67721efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg		return -EINVAL;
67821efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg
6794f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	switch (request->type) {
680c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	case FW_ISO_CONTEXT_RECEIVE:
6814f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->header_size < 4 || (request->header_size & 3))
682c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg			return -EINVAL;
68398b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg
684c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg		break;
685c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
686c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	case FW_ISO_CONTEXT_TRANSMIT:
6874f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->speed > SCODE_3200)
688c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg			return -EINVAL;
689c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
690c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg		break;
691c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
692c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	default:
69321efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg		return -EINVAL;
694c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	}
695c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
69624315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	context =  fw_iso_context_create(client->device->card,
69724315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg					 request->type,
69824315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg					 request->channel,
69924315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg					 request->speed,
70024315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg					 request->header_size,
70124315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg					 iso_callback, client);
70224315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	if (IS_ERR(context))
70324315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg		return PTR_ERR(context);
70424315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg
705abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	client->iso_closure = request->closure;
70624315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	client->iso_context = context;
70719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
708abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	/* We only support one context at this time. */
709abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	request->handle = 0;
710abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
71119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
71219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
71319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7141ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg/* Macros for decoding the iso packet control header. */
7151ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_PAYLOAD_LENGTH(v)	((v) & 0xffff)
7161ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_INTERRUPT(v)	(((v) >> 16) & 0x01)
7171ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_SKIP(v)		(((v) >> 17) & 0x01)
7187a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_TAG(v)		(((v) >> 18) & 0x03)
7197a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_SY(v)		(((v) >> 20) & 0x0f)
7201ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_HEADER_LENGTH(v)	(((v) >> 24) & 0xff)
7211ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg
7224f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_queue_iso(struct client *client, void *buffer)
72319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
7244f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_queue_iso *request = buffer;
72519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_iso_packet __user *p, *end, *next;
7269b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg	struct fw_iso_context *ctx = client->iso_context;
727ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	unsigned long payload, buffer_end, header_length;
7281ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg	u32 control;
72919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	int count;
73019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct {
73119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		struct fw_iso_packet packet;
73219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		u8 header[256];
73319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	} u;
73419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
735abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	if (ctx == NULL || request->handle != 0)
73619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
73719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
738c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	/*
739c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * If the user passes a non-NULL data pointer, has mmap()'ed
74019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * the iso buffer, and the pointer points inside the buffer,
74119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * we setup the payload pointers accordingly.  Otherwise we
7429aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	 * set them both to 0, which will still let packets with
74319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * payload_length == 0 through.  In other words, if no packets
74419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * use the indirect payload, the iso buffer need not be mapped
745c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * and the request->data pointer is ignored.
746c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 */
74719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7484f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	payload = (unsigned long)request->data - client->vm_start;
749ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	buffer_end = client->buffer.page_count << PAGE_SHIFT;
7504f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->data == 0 || client->buffer.pages == NULL ||
751ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	    payload >= buffer_end) {
7529aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		payload = 0;
753ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg		buffer_end = 0;
75419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
75519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7561ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro	p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets);
7571ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro
7581ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro	if (!access_ok(VERIFY_READ, p, request->size))
75919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EFAULT;
76019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7614f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	end = (void __user *)p + request->size;
76219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	count = 0;
76319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	while (p < end) {
7641ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		if (get_user(control, &p->control))
76519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EFAULT;
7661ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.payload_length = GET_PAYLOAD_LENGTH(control);
7671ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.interrupt = GET_INTERRUPT(control);
7681ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.skip = GET_SKIP(control);
7691ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.tag = GET_TAG(control);
7701ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.sy = GET_SY(control);
7711ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.header_length = GET_HEADER_LENGTH(control);
772295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg
7739b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg		if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) {
774295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			header_length = u.packet.header_length;
775295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		} else {
776c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			/*
777c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 * We require that header_length is a multiple of
778c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 * the fixed header size, ctx->header_size.
779c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 */
7809b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			if (ctx->header_size == 0) {
7819b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg				if (u.packet.header_length > 0)
7829b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg					return -EINVAL;
7839b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			} else if (u.packet.header_length % ctx->header_size != 0) {
784295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg				return -EINVAL;
7859b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			}
786295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			header_length = 0;
787295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		}
788295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg
78919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		next = (struct fw_cdev_iso_packet __user *)
790295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			&p->header[header_length / 4];
79119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		if (next > end)
79219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
79319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		if (__copy_from_user
794295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		    (u.packet.header, p->header, header_length))
79519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EFAULT;
79698b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg		if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
79719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		    u.packet.header_length + u.packet.payload_length > 0)
79819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
799ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg		if (payload + u.packet.payload_length > buffer_end)
80019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
80119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8029b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg		if (fw_iso_context_queue(ctx, &u.packet,
8039b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg					 &client->buffer, payload))
80419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			break;
80519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
80619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		p = next;
80719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		payload += u.packet.payload_length;
80819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		count++;
80919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
81019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8114f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->size    -= uptr_to_u64(p) - request->packets;
8124f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->packets  = uptr_to_u64(p);
8134f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->data     = client->vm_start + payload;
81419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
81519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return count;
81619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
81719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8184f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_start_iso(struct client *client, void *buffer)
81919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
8204f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_start_iso *request = buffer;
82119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
822fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter	if (client->iso_context == NULL || request->handle != 0)
823abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg		return -EINVAL;
824fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter
825eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg	if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) {
8264f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->tags == 0 || request->tags > 15)
827eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg			return -EINVAL;
828eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg
8294f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->sync > 15)
830eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg			return -EINVAL;
831eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg	}
832eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg
8334f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	return fw_iso_context_start(client->iso_context, request->cycle,
8344f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg				    request->sync, request->tags);
83519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
83619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8374f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_stop_iso(struct client *client, void *buffer)
838b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg{
839abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	struct fw_cdev_stop_iso *request = buffer;
840abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
841fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter	if (client->iso_context == NULL || request->handle != 0)
842abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg		return -EINVAL;
843abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
844b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg	return fw_iso_context_stop(client->iso_context);
845b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg}
846b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg
847a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richterstatic int ioctl_get_cycle_timer(struct client *client, void *buffer)
848a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter{
849a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	struct fw_cdev_get_cycle_timer *request = buffer;
850a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	struct fw_card *card = client->device->card;
851a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	unsigned long long bus_time;
852a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	struct timeval tv;
853a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	unsigned long flags;
854a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
855a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	preempt_disable();
856a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	local_irq_save(flags);
857a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
858a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	bus_time = card->driver->get_bus_time(card);
859a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	do_gettimeofday(&tv);
860a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
861a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	local_irq_restore(flags);
862a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	preempt_enable();
863a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
864a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	request->local_time = tv.tv_sec * 1000000ULL + tv.tv_usec;
865a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	request->cycle_timer = bus_time & 0xffffffff;
866a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	return 0;
867a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter}
868a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
8694f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
8704f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_get_info,
8714f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_send_request,
8724f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_allocate,
8734f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_deallocate,
8744f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_send_response,
8754f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_initiate_bus_reset,
8764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_add_descriptor,
8774f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_remove_descriptor,
8784f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_create_iso_context,
8794f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_queue_iso,
8804f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_start_iso,
8814f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_stop_iso,
882a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	ioctl_get_cycle_timer,
8834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg};
8844f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
88519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int
88619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergdispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
88719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
8884f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	char buffer[256];
8894f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	int retval;
8904f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
8914f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_TYPE(cmd) != '#' ||
8924f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers))
89319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
8944f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
8954f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_DIR(cmd) & _IOC_WRITE) {
8962d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (_IOC_SIZE(cmd) > sizeof(buffer) ||
8974f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		    copy_from_user(buffer, arg, _IOC_SIZE(cmd)))
8984f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			return -EFAULT;
8994f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	}
9004f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
9014f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	retval = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
9024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (retval < 0)
9034f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		return retval;
9044f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
9054f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_DIR(cmd) & _IOC_READ) {
9062d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (_IOC_SIZE(cmd) > sizeof(buffer) ||
9074f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		    copy_to_user(arg, buffer, _IOC_SIZE(cmd)))
9084f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			return -EFAULT;
90919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
9104f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
91199692f71ee04c6f249d0bf6a581359f32f409a38Stefan Richter	return retval;
91219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
91319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
91419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic long
91519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergfw_device_op_ioctl(struct file *file,
91619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		   unsigned int cmd, unsigned long arg)
91719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
91819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
91919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
920551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(client->device))
921551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
922551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
92319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dispatch_ioctl(client, cmd, (void __user *) arg);
92419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
92519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
92619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT
92719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic long
92819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergfw_device_op_compat_ioctl(struct file *file,
92919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			  unsigned int cmd, unsigned long arg)
93019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
93119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
93219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
933551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(client->device))
934551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
935551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
93619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dispatch_ioctl(client, cmd, compat_ptr(arg));
93719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
93819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif
93919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
94019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
94119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
94219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
9439aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	enum dma_data_direction direction;
9449aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	unsigned long size;
9459aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	int page_count, retval;
9469aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
947551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(client->device))
948551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
949551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
9509aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	/* FIXME: We could support multiple buffers, but we don't. */
9519aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (client->buffer.pages != NULL)
9529aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EBUSY;
9539aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
9549aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (!(vma->vm_flags & VM_SHARED))
9559aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EINVAL;
95619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9579aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (vma->vm_start & ~PAGE_MASK)
95819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
95919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
96019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	client->vm_start = vma->vm_start;
9619aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	size = vma->vm_end - vma->vm_start;
9629aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	page_count = size >> PAGE_SHIFT;
9639aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (size & ~PAGE_MASK)
9649aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EINVAL;
9659aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
9669aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (vma->vm_flags & VM_WRITE)
9679aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		direction = DMA_TO_DEVICE;
9689aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	else
9699aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		direction = DMA_FROM_DEVICE;
9709aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
9719aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	retval = fw_iso_buffer_init(&client->buffer, client->device->card,
9729aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg				    page_count, direction);
9739aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (retval < 0)
9749aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return retval;
97519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9769aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	retval = fw_iso_buffer_map(&client->buffer, vma);
9779aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (retval < 0)
9789aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		fw_iso_buffer_destroy(&client->buffer, client->device->card);
9799aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
9809aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	return retval;
98119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
98219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
98319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_release(struct inode *inode, struct file *file)
98419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
98519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
9862603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	struct event *e, *next_e;
9873964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource *r, *next_r;
98819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9899aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (client->buffer.pages)
9909aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		fw_iso_buffer_destroy(&client->buffer, client->device->card);
9919aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
99219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (client->iso_context)
99319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		fw_iso_context_destroy(client->iso_context);
99419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9953964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	list_for_each_entry_safe(r, next_r, &client->resource_list, link)
9963964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		r->release(client, r);
99766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
998c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	/*
999c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * FIXME: We should wait for the async tasklets to stop
1000c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * running before freeing the memory.
1001c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 */
100228cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
10032603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	list_for_each_entry_safe(e, next_e, &client->event_list, link)
10042603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		kfree(e);
100519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1006d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_lock(&client->device->client_list_mutex);
100797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	list_del(&client->link);
1008d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_unlock(&client->device->client_list_mutex);
100997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
101019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	fw_device_put(client->device);
101119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(client);
101219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
101319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
101419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
101519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
101619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic unsigned int fw_device_op_poll(struct file *file, poll_table * pt)
101719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
101819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
10192603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	unsigned int mask = 0;
102019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
102119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	poll_wait(file, &client->wait, pt);
102219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10232603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (fw_device_is_shutdown(client->device))
10242603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		mask |= POLLHUP | POLLERR;
102519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (!list_empty(&client->event_list))
10262603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		mask |= POLLIN | POLLRDNORM;
10272603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
10282603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	return mask;
102919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
103019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
103121ebcd1224d05c8673053e1e93ab9ec7ef3e0b84Stefan Richterconst struct file_operations fw_device_ops = {
103219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.owner		= THIS_MODULE,
103319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.open		= fw_device_op_open,
103419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.read		= fw_device_op_read,
103519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.unlocked_ioctl	= fw_device_op_ioctl,
103619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.poll		= fw_device_op_poll,
103719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.release	= fw_device_op_release,
103819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.mmap		= fw_device_op_mmap,
103919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
104019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT
10415af4e5eab30d481f76b89a2167c873dfad960acbStefan Richter	.compat_ioctl	= fw_device_op_compat_ioctl,
104219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif
104319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
1044