core-cdev.c revision 2d826cc5c791bdc5f5651324c485746be9492be0
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>
2719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/poll.h>
2819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/delay.h>
2919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/mm.h>
30a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg#include <linux/idr.h>
3119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/compat.h>
329640d3d775aa325650c8fcdf49127542f77b2156Kristian Høgsberg#include <linux/firewire-cdev.h>
3319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <asm/uaccess.h>
3419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include "fw-transaction.h"
3519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include "fw-topology.h"
3619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include "fw-device.h"
3719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
383964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstruct client;
393964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstruct client_resource {
403964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct list_head link;
413964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	void (*release)(struct client *client, struct client_resource *r);
423964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	u32 handle;
433964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg};
443964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
45c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg/*
46c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * dequeue_event() just kfree()'s the event, so the event has to be
47c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg * the first field in the struct.
48c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg */
49c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg
5019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct event {
5119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct { void *data; size_t size; } v[2];
5219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct list_head link;
5319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
5419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergstruct bus_reset {
5697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct event event;
5797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct fw_cdev_event_bus_reset reset;
5897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg};
5997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
6019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct response {
6119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event event;
6219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_transaction transaction;
6319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client;
643964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource resource;
6519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_event_response response;
6619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
6719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct iso_interrupt {
6919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event event;
7019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_event_iso_interrupt interrupt;
7119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
7219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct client {
74344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	u32 version;
7519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device;
7619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spinlock_t lock;
7766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	u32 resource_handle;
783964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct list_head resource_list;
7919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct list_head event_list;
8019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	wait_queue_head_t wait;
81da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	u64 bus_reset_closure;
829aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
8319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_iso_context *iso_context;
84abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	u64 iso_closure;
859aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	struct fw_iso_buffer buffer;
869aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	unsigned long vm_start;
8797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
8897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct list_head link;
8919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
9019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic inline void __user *
9219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergu64_to_uptr(__u64 value)
9319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
9419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return (void __user *)(unsigned long)value;
9519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
9619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic inline __u64
9819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberguptr_to_u64(void __user *ptr)
9919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
10019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return (__u64)(unsigned long)ptr;
10119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
10219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_open(struct inode *inode, struct file *file)
10419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
10519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device;
10619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client;
10797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	unsigned long flags;
10819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
109a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg	device = fw_device_from_devt(inode->i_rdev);
110a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg	if (device == NULL)
111a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg		return -ENODEV;
11219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1132d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	client = kzalloc(sizeof(*client), GFP_KERNEL);
11419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (client == NULL)
11519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
11619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
11719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	client->device = fw_device_get(device);
11819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	INIT_LIST_HEAD(&client->event_list);
1193964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	INIT_LIST_HEAD(&client->resource_list);
12019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_lock_init(&client->lock);
12119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	init_waitqueue_head(&client->wait);
12219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
12319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	file->private_data = client;
12419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
12597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	spin_lock_irqsave(&device->card->lock, flags);
12697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	list_add_tail(&client->link, &device->client_list);
12797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	spin_unlock_irqrestore(&device->card->lock, flags);
12897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
12919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
13019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
13119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
13219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void queue_event(struct client *client, struct event *event,
13319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			void *data0, size_t size0, void *data1, size_t size1)
13419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
13519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	unsigned long flags;
13619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
13719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[0].data = data0;
13819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[0].size = size0;
13919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[1].data = data1;
14019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[1].size = size1;
14119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
14219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
14319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
14419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	list_add_tail(&event->link, &client->event_list);
14519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	wake_up_interruptible(&client->wait);
14619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
14719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
14819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
14919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1502603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic int
1512603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergdequeue_event(struct client *client, char __user *buffer, size_t count)
15219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
15319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	unsigned long flags;
15419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event *event;
15519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	size_t size, total;
1562603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	int i, retval;
15719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1582603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	retval = wait_event_interruptible(client->wait,
1592603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg					  !list_empty(&client->event_list) ||
1602603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg					  fw_device_is_shutdown(client->device));
1612603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (retval < 0)
1622603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		return retval;
16319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1642603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (list_empty(&client->event_list) &&
1652603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		       fw_device_is_shutdown(client->device))
1662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		return -ENODEV;
16719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1682603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
16919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event = container_of(client->event_list.next, struct event, link);
17019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	list_del(&event->link);
17119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
17219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
17319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	total = 0;
17419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
17519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		size = min(event->v[i].size, count - total);
1762603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		if (copy_to_user(buffer + total, event->v[i].data, size)) {
1772603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg			retval = -EFAULT;
17819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			goto out;
1792603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		}
18019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		total += size;
18119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
18219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	retval = total;
18319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
18419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg out:
18519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(event);
18619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
18719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return retval;
18819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
18919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
19019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic ssize_t
19119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergfw_device_op_read(struct file *file,
19219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		  char __user *buffer, size_t count, loff_t *offset)
19319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
19419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
19519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
19619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dequeue_event(client, buffer, count);
19719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
19819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
19997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergstatic void
200344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsbergfill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
201da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg		     struct client *client)
202344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg{
203da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	struct fw_card *card = client->device->card;
204344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
205da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	event->closure	     = client->bus_reset_closure;
206344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->type          = FW_CDEV_EVENT_BUS_RESET;
207da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	event->node_id       = client->device->node_id;
208344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->local_node_id = card->local_node->node_id;
209344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->bm_node_id    = 0; /* FIXME: We don't track the BM. */
210344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->irm_node_id   = card->irm_node->node_id;
211344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->root_node_id  = card->root_node->node_id;
212344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->generation    = card->generation;
213344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg}
214344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
215344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsbergstatic void
2162603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergfor_each_client(struct fw_device *device,
2172603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		void (*callback)(struct client *client))
2182603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
2192603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	struct fw_card *card = device->card;
2202603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	struct client *c;
2212603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	unsigned long flags;
2222603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
2232603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	spin_lock_irqsave(&card->lock, flags);
2242603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
2252603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	list_for_each_entry(c, &device->client_list, link)
2262603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		callback(c);
2272603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
2282603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	spin_unlock_irqrestore(&card->lock, flags);
2292603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
2302603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
2312603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void
23297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergqueue_bus_reset_event(struct client *client)
23397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{
23497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct bus_reset *bus_reset;
23597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
2362d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	bus_reset = kzalloc(sizeof(*bus_reset), GFP_ATOMIC);
23797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	if (bus_reset == NULL) {
23897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg		fw_notify("Out of memory when allocating bus reset event\n");
23997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg		return;
24097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	}
24197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
242da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	fill_bus_reset_event(&bus_reset->reset, client);
24397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
24497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	queue_event(client, &bus_reset->event,
2452d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		    &bus_reset->reset, sizeof(bus_reset->reset), NULL, 0);
24697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg}
24797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
24897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergvoid fw_device_cdev_update(struct fw_device *device)
24997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{
2502603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	for_each_client(device, queue_bus_reset_event);
2512603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
25297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
2532603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void wake_up_client(struct client *client)
2542603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
2552603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	wake_up_interruptible(&client->wait);
2562603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
25797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
2582603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergvoid fw_device_cdev_remove(struct fw_device *device)
2592603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
2602603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	for_each_client(device, wake_up_client);
26197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg}
26297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
2634f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_get_info(struct client *client, void *buffer)
26419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
2654f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_get_info *get_info = buffer;
266344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	struct fw_cdev_event_bus_reset bus_reset;
267344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
2684f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	client->version = get_info->version;
2694f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	get_info->version = FW_CDEV_VERSION;
270344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
2714f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (get_info->rom != 0) {
2724f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		void __user *uptr = u64_to_uptr(get_info->rom);
2734f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		size_t want = get_info->rom_length;
274d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter		size_t have = client->device->config_rom_length * 4;
275344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
276d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter		if (copy_to_user(uptr, client->device->config_rom,
277d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter				 min(want, have)))
278344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg			return -EFAULT;
279344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	}
2804f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	get_info->rom_length = client->device->config_rom_length * 4;
281344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
2824f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	client->bus_reset_closure = get_info->bus_reset_closure;
2834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (get_info->bus_reset != 0) {
2844f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		void __user *uptr = u64_to_uptr(get_info->bus_reset);
285344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
286da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg		fill_bus_reset_event(&bus_reset, client);
2872d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset)))
288344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg			return -EFAULT;
289344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	}
29019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2914f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	get_info->card = client->device->card->index;
29219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
29319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
29419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
29519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
29619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void
2973964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergadd_client_resource(struct client *client, struct client_resource *resource)
2983964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
2993964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	unsigned long flags;
3003964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3013964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
3023964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	list_add_tail(&resource->link, &client->resource_list);
3033964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	resource->handle = client->resource_handle++;
3043964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
3053964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
3063964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3073964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic int
3083964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergrelease_client_resource(struct client *client, u32 handle,
3093964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			struct client_resource **resource)
3103964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
3113964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource *r;
3123964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	unsigned long flags;
3133964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3143964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
3153964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	list_for_each_entry(r, &client->resource_list, link) {
3163964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		if (r->handle == handle) {
3173964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			list_del(&r->link);
3183964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			break;
3193964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		}
3203964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	}
3213964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
3223964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3233964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	if (&r->link == &client->resource_list)
3243964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		return -EINVAL;
3253964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3263964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	if (resource)
3273964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		*resource = r;
3283964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	else
3293964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		r->release(client, r);
3303964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3313964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	return 0;
3323964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
3333964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3343964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void
3353964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergrelease_transaction(struct client *client, struct client_resource *resource)
3363964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
3373964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct response *response =
3383964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		container_of(resource, struct response, resource);
3393964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3403964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	fw_cancel_transaction(client->device->card, &response->transaction);
3413964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
3423964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
3433964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void
34419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergcomplete_transaction(struct fw_card *card, int rcode,
34519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		     void *payload, size_t length, void *data)
34619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
34719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct response *response = data;
34819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = response->client;
34928cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	unsigned long flags;
35019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
35119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (length < response->response.length)
35219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		response->response.length = length;
35319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (rcode == RCODE_COMPLETE)
35419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		memcpy(response->response.data, payload,
35519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		       response->response.length);
35619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
35728cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
3583964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	list_del(&response->resource.link);
35928cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
36028cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
36119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	response->response.type   = FW_CDEV_EVENT_RESPONSE;
36219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	response->response.rcode  = rcode;
36319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	queue_event(client, &response->event,
3642d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		    &response->response, sizeof(response->response),
36519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		    response->response.data, response->response.length);
36619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
36719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
3684f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic ssize_t ioctl_send_request(struct client *client, void *buffer)
36919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
37019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device = client->device;
3714f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_send_request *request = buffer;
37219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct response *response;
37319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
37419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	/* What is the biggest size we'll accept, really? */
3754f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->length > 4096)
37619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
37719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
3782d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	response = kmalloc(sizeof(*response) + request->length, GFP_KERNEL);
37919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (response == NULL)
38019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
38119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
38219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	response->client = client;
3834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	response->response.length = request->length;
3844f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	response->response.closure = request->closure;
38519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
3864f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->data &&
38719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	    copy_from_user(response->response.data,
3884f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			   u64_to_uptr(request->data), request->length)) {
38919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		kfree(response);
39019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EFAULT;
39119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
39219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
3933964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	response->resource.release = release_transaction;
3943964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	add_client_resource(client, &response->resource);
39528cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
39619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	fw_send_request(device->card, &response->transaction,
3974f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			request->tcode & 0x1f,
398907293d78872ee492ce6a114258dd853ec5082aeStefan Richter			device->node->node_id,
3994f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			request->generation,
40019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			device->node->max_speed,
4014f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			request->offset,
4024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			response->response.data, request->length,
40319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			complete_transaction, response);
40419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4054f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->data)
4062d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		return sizeof(request) + request->length;
40719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	else
4082d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		return sizeof(request);
40919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
41019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
41119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct address_handler {
41219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_address_handler handler;
41319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	__u64 closure;
41419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client;
4153964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource resource;
41619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
41719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
41819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct request {
41919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_request *request;
42019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	void *data;
42119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	size_t length;
4223964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource resource;
42319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
42419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
42519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct request_event {
42619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event event;
42719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_event_request request;
42819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
42919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
43019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void
4313964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergrelease_request(struct client *client, struct client_resource *resource)
4323964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
4333964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct request *request =
4343964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		container_of(resource, struct request, resource);
4353964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
4363964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	fw_send_response(client->device->card, request->request,
4373964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			 RCODE_CONFLICT_ERROR);
4383964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	kfree(request);
4393964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
4403964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
4413964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void
44219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberghandle_request(struct fw_card *card, struct fw_request *r,
44319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	       int tcode, int destination, int source,
44419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	       int generation, int speed,
44519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	       unsigned long long offset,
44619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	       void *payload, size_t length, void *callback_data)
44719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
44819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct address_handler *handler = callback_data;
44919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct request *request;
45019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct request_event *e;
45119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = handler->client;
45219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4532d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	request = kmalloc(sizeof(*request), GFP_ATOMIC);
4542d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	e = kmalloc(sizeof(*e), GFP_ATOMIC);
45519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (request == NULL || e == NULL) {
45619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		kfree(request);
45719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		kfree(e);
45819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		fw_send_response(card, r, RCODE_CONFLICT_ERROR);
45919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return;
46019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
46119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
46219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	request->request = r;
46319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	request->data    = payload;
46419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	request->length  = length;
46519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4663964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	request->resource.release = release_request;
4673964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	add_client_resource(client, &request->resource);
46819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
46919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.type    = FW_CDEV_EVENT_REQUEST;
47019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.tcode   = tcode;
47119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.offset  = offset;
47219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.length  = length;
4733964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	e->request.handle  = request->resource.handle;
47419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.closure = handler->closure;
47519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
47619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	queue_event(client, &e->event,
4772d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		    &e->request, sizeof(e->request), payload, length);
47819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
47919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4803964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void
4813964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergrelease_address_handler(struct client *client,
4823964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			struct client_resource *resource)
4833964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
4843964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct address_handler *handler =
4853964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		container_of(resource, struct address_handler, resource);
4863964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
4873964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	fw_core_remove_address_handler(&handler->handler);
4883964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	kfree(handler);
4893964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
4903964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
4914f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_allocate(struct client *client, void *buffer)
49219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
4934f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_allocate *request = buffer;
49419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct address_handler *handler;
49519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_address_region region;
49619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4972d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	handler = kmalloc(sizeof(*handler), GFP_KERNEL);
49819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (handler == NULL)
49919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
50019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5014f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	region.start = request->offset;
5024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	region.end = request->offset + request->length;
5034f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	handler->handler.length = request->length;
50419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	handler->handler.address_callback = handle_request;
50519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	handler->handler.callback_data = handler;
5064f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	handler->closure = request->closure;
50719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	handler->client = client;
50819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
50919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (fw_core_add_address_handler(&handler->handler, &region) < 0) {
51019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		kfree(handler);
51119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EBUSY;
51219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
51319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5143964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	handler->resource.release = release_address_handler;
5153964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	add_client_resource(client, &handler->resource);
5164f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->handle = handler->resource.handle;
51719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
51819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
51919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
52019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5214f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_deallocate(struct client *client, void *buffer)
5229472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg{
5234f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_deallocate *request = buffer;
5249472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg
5254f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	return release_client_resource(client, request->handle, NULL);
5269472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg}
5279472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg
5284f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_send_response(struct client *client, void *buffer)
52919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
5304f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_send_response *request = buffer;
5313964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource *resource;
53219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct request *r;
53319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5344f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (release_client_resource(client, request->handle, &resource) < 0)
53519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
5363964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	r = container_of(resource, struct request, resource);
5374f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->length < r->length)
5384f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		r->length = request->length;
5394f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (copy_from_user(r->data, u64_to_uptr(request->data), r->length))
54019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EFAULT;
54119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5424f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	fw_send_response(client->device->card, r->request, request->rcode);
54319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(r);
54419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
54519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
54619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
54719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5484f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_initiate_bus_reset(struct client *client, void *buffer)
5495371842b723dd04df57171f2c74660966901380cKristian Høgsberg{
5504f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_initiate_bus_reset *request = buffer;
5515371842b723dd04df57171f2c74660966901380cKristian Høgsberg	int short_reset;
5525371842b723dd04df57171f2c74660966901380cKristian Høgsberg
5534f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	short_reset = (request->type == FW_CDEV_SHORT_RESET);
5545371842b723dd04df57171f2c74660966901380cKristian Høgsberg
5555371842b723dd04df57171f2c74660966901380cKristian Høgsberg	return fw_core_initiate_bus_reset(client->device->card, short_reset);
5565371842b723dd04df57171f2c74660966901380cKristian Høgsberg}
5575371842b723dd04df57171f2c74660966901380cKristian Høgsberg
55866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsbergstruct descriptor {
55966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	struct fw_descriptor d;
5603964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource resource;
56166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	u32 data[0];
56266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg};
56366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
5643964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void release_descriptor(struct client *client,
5653964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			       struct client_resource *resource)
5663964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
5673964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct descriptor *descriptor =
5683964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		container_of(resource, struct descriptor, resource);
5693964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
5703964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	fw_core_remove_descriptor(&descriptor->d);
5713964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	kfree(descriptor);
5723964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
5733964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
5744f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_add_descriptor(struct client *client, void *buffer)
57566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{
5764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_add_descriptor *request = buffer;
57766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	struct descriptor *descriptor;
57866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	int retval;
57966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
5804f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->length > 256)
58166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -EINVAL;
58266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
58366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	descriptor =
5842d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		kmalloc(sizeof(*descriptor) + request->length * 4, GFP_KERNEL);
58566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	if (descriptor == NULL)
58666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -ENOMEM;
58766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
58866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	if (copy_from_user(descriptor->data,
5894f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			   u64_to_uptr(request->data), request->length * 4)) {
59066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		kfree(descriptor);
59166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -EFAULT;
59266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	}
59366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
5944f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	descriptor->d.length = request->length;
5954f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	descriptor->d.immediate = request->immediate;
5964f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	descriptor->d.key = request->key;
59766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	descriptor->d.data = descriptor->data;
59866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
59966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	retval = fw_core_add_descriptor(&descriptor->d);
60066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	if (retval < 0) {
60166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		kfree(descriptor);
60266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return retval;
60366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	}
60466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
6053964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	descriptor->resource.release = release_descriptor;
6063964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	add_client_resource(client, &descriptor->resource);
6074f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->handle = descriptor->resource.handle;
60866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
60966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	return 0;
61066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg}
61166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
6124f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_remove_descriptor(struct client *client, void *buffer)
61366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{
6144f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_remove_descriptor *request = buffer;
61566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
6164f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	return release_client_resource(client, request->handle, NULL);
61766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg}
61866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
61919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void
6209b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsbergiso_callback(struct fw_iso_context *context, u32 cycle,
6219b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg	     size_t header_length, void *header, void *data)
62219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
62319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = data;
62419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct iso_interrupt *interrupt;
62519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6262d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	interrupt = kzalloc(sizeof(*interrupt) + header_length, GFP_ATOMIC);
62719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (interrupt == NULL)
62819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return;
62919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
63019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	interrupt->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
631abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	interrupt->interrupt.closure   = client->iso_closure;
63219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	interrupt->interrupt.cycle     = cycle;
6339b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg	interrupt->interrupt.header_length = header_length;
6349b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg	memcpy(interrupt->interrupt.header, header, header_length);
63519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	queue_event(client, &interrupt->event,
6369b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg		    &interrupt->interrupt,
6372d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		    sizeof(interrupt->interrupt) + header_length, NULL, 0);
63819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
63919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6404f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_create_iso_context(struct client *client, void *buffer)
64119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
6424f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_create_iso_context *request = buffer;
64319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6444f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->channel > 63)
64521efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg		return -EINVAL;
64621efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg
6474f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	switch (request->type) {
648c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	case FW_ISO_CONTEXT_RECEIVE:
6494f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->header_size < 4 || (request->header_size & 3))
650c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg			return -EINVAL;
65198b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg
652c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg		break;
653c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
654c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	case FW_ISO_CONTEXT_TRANSMIT:
6554f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->speed > SCODE_3200)
656c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg			return -EINVAL;
657c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
658c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg		break;
659c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
660c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	default:
66121efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg		return -EINVAL;
662c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	}
663c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
664abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	client->iso_closure = request->closure;
66519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	client->iso_context = fw_iso_context_create(client->device->card,
6664f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg						    request->type,
6674f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg						    request->channel,
6684f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg						    request->speed,
6694f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg						    request->header_size,
67019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg						    iso_callback, client);
67119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (IS_ERR(client->iso_context))
67219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return PTR_ERR(client->iso_context);
67319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
674abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	/* We only support one context at this time. */
675abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	request->handle = 0;
676abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
67719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
67819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
67919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6804f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_queue_iso(struct client *client, void *buffer)
68119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
6824f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_queue_iso *request = buffer;
68319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_iso_packet __user *p, *end, *next;
6849b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg	struct fw_iso_context *ctx = client->iso_context;
685ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	unsigned long payload, buffer_end, header_length;
68619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	int count;
68719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct {
68819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		struct fw_iso_packet packet;
68919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		u8 header[256];
69019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	} u;
69119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
692abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	if (ctx == NULL || request->handle != 0)
69319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
69419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
695c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	/*
696c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * If the user passes a non-NULL data pointer, has mmap()'ed
69719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * the iso buffer, and the pointer points inside the buffer,
69819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * we setup the payload pointers accordingly.  Otherwise we
6999aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	 * set them both to 0, which will still let packets with
70019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * payload_length == 0 through.  In other words, if no packets
70119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * use the indirect payload, the iso buffer need not be mapped
702c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * and the request->data pointer is ignored.
703c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 */
70419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7054f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	payload = (unsigned long)request->data - client->vm_start;
706ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	buffer_end = client->buffer.page_count << PAGE_SHIFT;
7074f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->data == 0 || client->buffer.pages == NULL ||
708ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	    payload >= buffer_end) {
7099aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		payload = 0;
710ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg		buffer_end = 0;
71119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
71219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7134f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (!access_ok(VERIFY_READ, request->packets, request->size))
71419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EFAULT;
71519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7164f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets);
7174f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	end = (void __user *)p + request->size;
71819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	count = 0;
71919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	while (p < end) {
7202d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (__copy_from_user(&u.packet, p, sizeof(*p)))
72119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EFAULT;
722295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg
7239b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg		if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) {
724295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			header_length = u.packet.header_length;
725295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		} else {
726c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			/*
727c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 * We require that header_length is a multiple of
728c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 * the fixed header size, ctx->header_size.
729c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 */
7309b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			if (ctx->header_size == 0) {
7319b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg				if (u.packet.header_length > 0)
7329b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg					return -EINVAL;
7339b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			} else if (u.packet.header_length % ctx->header_size != 0) {
734295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg				return -EINVAL;
7359b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			}
736295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			header_length = 0;
737295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		}
738295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg
73919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		next = (struct fw_cdev_iso_packet __user *)
740295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			&p->header[header_length / 4];
74119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		if (next > end)
74219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
74319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		if (__copy_from_user
744295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		    (u.packet.header, p->header, header_length))
74519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EFAULT;
74698b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg		if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
74719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		    u.packet.header_length + u.packet.payload_length > 0)
74819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
749ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg		if (payload + u.packet.payload_length > buffer_end)
75019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
75119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7529b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg		if (fw_iso_context_queue(ctx, &u.packet,
7539b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg					 &client->buffer, payload))
75419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			break;
75519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
75619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		p = next;
75719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		payload += u.packet.payload_length;
75819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		count++;
75919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
76019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7614f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->size    -= uptr_to_u64(p) - request->packets;
7624f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->packets  = uptr_to_u64(p);
7634f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->data     = client->vm_start + payload;
76419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
76519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return count;
76619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
76719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7684f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_start_iso(struct client *client, void *buffer)
76919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
7704f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_start_iso *request = buffer;
77119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
772abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	if (request->handle != 0)
773abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg		return -EINVAL;
774eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg	if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) {
7754f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->tags == 0 || request->tags > 15)
776eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg			return -EINVAL;
777eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg
7784f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->sync > 15)
779eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg			return -EINVAL;
780eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg	}
781eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg
7824f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	return fw_iso_context_start(client->iso_context, request->cycle,
7834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg				    request->sync, request->tags);
78419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
78519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7864f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_stop_iso(struct client *client, void *buffer)
787b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg{
788abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	struct fw_cdev_stop_iso *request = buffer;
789abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
790abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	if (request->handle != 0)
791abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg		return -EINVAL;
792abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
793b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg	return fw_iso_context_stop(client->iso_context);
794b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg}
795b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg
7964f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
7974f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_get_info,
7984f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_send_request,
7994f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_allocate,
8004f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_deallocate,
8014f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_send_response,
8024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_initiate_bus_reset,
8034f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_add_descriptor,
8044f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_remove_descriptor,
8054f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_create_iso_context,
8064f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_queue_iso,
8074f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_start_iso,
8084f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_stop_iso,
8094f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg};
8104f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
81119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int
81219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergdispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
81319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
8144f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	char buffer[256];
8154f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	int retval;
8164f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
8174f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_TYPE(cmd) != '#' ||
8184f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers))
81919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
8204f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
8214f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_DIR(cmd) & _IOC_WRITE) {
8222d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (_IOC_SIZE(cmd) > sizeof(buffer) ||
8234f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		    copy_from_user(buffer, arg, _IOC_SIZE(cmd)))
8244f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			return -EFAULT;
8254f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	}
8264f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
8274f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	retval = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
8284f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (retval < 0)
8294f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		return retval;
8304f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
8314f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_DIR(cmd) & _IOC_READ) {
8322d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (_IOC_SIZE(cmd) > sizeof(buffer) ||
8334f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		    copy_to_user(arg, buffer, _IOC_SIZE(cmd)))
8344f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			return -EFAULT;
83519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
8364f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
8374f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	return 0;
83819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
83919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
84019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic long
84119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergfw_device_op_ioctl(struct file *file,
84219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		   unsigned int cmd, unsigned long arg)
84319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
84419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
84519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
84619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dispatch_ioctl(client, cmd, (void __user *) arg);
84719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
84819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
84919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT
85019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic long
85119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergfw_device_op_compat_ioctl(struct file *file,
85219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			  unsigned int cmd, unsigned long arg)
85319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
85419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
85519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
85619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dispatch_ioctl(client, cmd, compat_ptr(arg));
85719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
85819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif
85919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
86019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
86119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
86219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
8639aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	enum dma_data_direction direction;
8649aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	unsigned long size;
8659aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	int page_count, retval;
8669aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
8679aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	/* FIXME: We could support multiple buffers, but we don't. */
8689aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (client->buffer.pages != NULL)
8699aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EBUSY;
8709aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
8719aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (!(vma->vm_flags & VM_SHARED))
8729aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EINVAL;
87319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8749aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (vma->vm_start & ~PAGE_MASK)
87519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
87619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
87719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	client->vm_start = vma->vm_start;
8789aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	size = vma->vm_end - vma->vm_start;
8799aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	page_count = size >> PAGE_SHIFT;
8809aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (size & ~PAGE_MASK)
8819aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EINVAL;
8829aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
8839aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (vma->vm_flags & VM_WRITE)
8849aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		direction = DMA_TO_DEVICE;
8859aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	else
8869aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		direction = DMA_FROM_DEVICE;
8879aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
8889aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	retval = fw_iso_buffer_init(&client->buffer, client->device->card,
8899aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg				    page_count, direction);
8909aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (retval < 0)
8919aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return retval;
89219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8939aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	retval = fw_iso_buffer_map(&client->buffer, vma);
8949aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (retval < 0)
8959aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		fw_iso_buffer_destroy(&client->buffer, client->device->card);
8969aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
8979aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	return retval;
89819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
89919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
90019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_release(struct inode *inode, struct file *file)
90119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
90219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
9032603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	struct event *e, *next_e;
9043964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource *r, *next_r;
90597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	unsigned long flags;
90619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9079aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (client->buffer.pages)
9089aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		fw_iso_buffer_destroy(&client->buffer, client->device->card);
9099aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
91019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (client->iso_context)
91119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		fw_iso_context_destroy(client->iso_context);
91219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9133964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	list_for_each_entry_safe(r, next_r, &client->resource_list, link)
9143964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		r->release(client, r);
91566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
916c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	/*
917c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * FIXME: We should wait for the async tasklets to stop
918c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * running before freeing the memory.
919c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 */
92028cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
9212603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	list_for_each_entry_safe(e, next_e, &client->event_list, link)
9222603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		kfree(e);
92319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
92497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	spin_lock_irqsave(&client->device->card->lock, flags);
92597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	list_del(&client->link);
92697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	spin_unlock_irqrestore(&client->device->card->lock, flags);
92797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
92819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	fw_device_put(client->device);
92919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(client);
93019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
93119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
93219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
93319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
93419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic unsigned int fw_device_op_poll(struct file *file, poll_table * pt)
93519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
93619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
9372603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	unsigned int mask = 0;
93819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
93919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	poll_wait(file, &client->wait, pt);
94019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9412603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (fw_device_is_shutdown(client->device))
9422603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		mask |= POLLHUP | POLLERR;
94319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (!list_empty(&client->event_list))
9442603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		mask |= POLLIN | POLLRDNORM;
9452603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
9462603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	return mask;
94719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
94819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
94921ebcd1224d05c8673053e1e93ab9ec7ef3e0b84Stefan Richterconst struct file_operations fw_device_ops = {
95019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.owner		= THIS_MODULE,
95119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.open		= fw_device_op_open,
95219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.read		= fw_device_op_read,
95319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.unlocked_ioctl	= fw_device_op_ioctl,
95419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.poll		= fw_device_op_poll,
95519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.release	= fw_device_op_release,
95619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.mmap		= fw_device_op_mmap,
95719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
95819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT
9595af4e5eab30d481f76b89a2167c873dfad960acbStefan Richter	.compat_ioctl	= fw_device_op_compat_ioctl,
96019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif
96119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
962