core-cdev.c revision 168cf9af699e87d5a6f44b684583714ecabb8e71
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
21be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/compat.h>
22be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/delay.h>
23be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/device.h>
24be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/errno.h>
2577c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include <linux/firewire.h>
26be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/firewire-cdev.h>
27be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/idr.h>
284a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter#include <linux/irqflags.h>
29b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/jiffies.h>
3019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/kernel.h>
31fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter#include <linux/kref.h>
32be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/mm.h>
33be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/module.h>
34d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter#include <linux/mutex.h>
3519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/poll.h>
36a99bbaf5ee6bad1aca0c88ea65ec6e5373e86184Alexey Dobriyan#include <linux/sched.h>
37cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason#include <linux/spinlock.h>
38be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/time.h>
39e034d242593f12533c11742ce38c245a33e57dc7Stefan Richter#include <linux/uaccess.h>
40be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/vmalloc.h>
41be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/wait.h>
42b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/workqueue.h>
43be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter
44a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter#include <asm/system.h>
45be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter
4677c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include "core.h"
4719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct client {
49344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	u32 version;
5019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device;
5145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
5219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spinlock_t lock;
5345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	bool in_shutdown;
5445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	struct idr resource_idr;
5519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct list_head event_list;
5619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	wait_queue_head_t wait;
57da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	u64 bus_reset_closure;
589aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
5919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_iso_context *iso_context;
60abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	u64 iso_closure;
619aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	struct fw_iso_buffer buffer;
629aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	unsigned long vm_start;
6397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
6497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct list_head link;
65fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	struct kref kref;
6619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
6719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
68fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic inline void client_get(struct client *client)
69fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{
70fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kref_get(&client->kref);
71fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter}
72fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
73fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_release(struct kref *kref)
74fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{
75fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	struct client *client = container_of(kref, struct client, kref);
76fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
77fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	fw_device_put(client->device);
78fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kfree(client);
79fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter}
80fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
81fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_put(struct client *client)
82fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{
83fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kref_put(&client->kref, client_release);
84fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter}
85fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
8697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource;
8797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richtertypedef void (*client_resource_release_fn_t)(struct client *,
8897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter					     struct client_resource *);
8997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource {
9097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	client_resource_release_fn_t release;
9197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	int handle;
9297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
9397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
9497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct address_handler_resource {
9597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
9697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_address_handler handler;
9797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	__u64 closure;
9897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client *client;
9997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
10097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
10197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_resource {
10297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
10397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_transaction transaction;
10497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
10597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
10697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_resource {
10797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
10897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_request *request;
10997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	void *data;
11097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	size_t length;
11197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
11297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
11397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct descriptor_resource {
11497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
11597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_descriptor descriptor;
11697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	u32 data[0];
11797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
11897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
119b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource {
120b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct client_resource resource;
121b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct client *client;
122b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/* Schedule work and access todo only with client->lock held. */
123b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct delayed_work work;
1241ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
1251ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	      ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
126b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	int generation;
127b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	u64 channels;
128b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	s32 bandwidth;
1296fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter	__be32 transaction_data[2];
130b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource_event *e_alloc, *e_dealloc;
131b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter};
132b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
133b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *, struct client_resource *);
134b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1359fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_iso_resource(struct iso_resource *r, unsigned long delay)
1369fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{
1379fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	client_get(r->client);
1389fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	if (!schedule_delayed_work(&r->work, delay))
1399fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		client_put(r->client);
1409fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter}
1419fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter
1429fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_if_iso_resource(struct client_resource *resource)
1439fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{
1449fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	if (resource->release == release_iso_resource)
1459fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_iso_resource(container_of(resource,
1469fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter					struct iso_resource, resource), 0);
1479fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter}
1489fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter
14997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter/*
15097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * dequeue_event() just kfree()'s the event, so the event has to be
15197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * the first field in a struct XYZ_event.
15297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter */
15397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct event {
15497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct { void *data; size_t size; } v[2];
15597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct list_head link;
15697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
15797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
15897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct bus_reset_event {
15997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
16097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_bus_reset reset;
16197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
16297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
16397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_event {
16497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
16597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client *client;
16697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_resource r;
16797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_response response;
16897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
16997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
17097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_event {
17197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
17297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_request request;
17397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
17497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
17597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct iso_interrupt_event {
17697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
17797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_iso_interrupt interrupt;
17897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
17997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
180b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource_event {
181b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct event event;
182e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct fw_cdev_event_iso_resource iso_resource;
183b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter};
184b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
18553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic inline void __user *u64_to_uptr(__u64 value)
18619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
18719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return (void __user *)(unsigned long)value;
18819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
18919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
19053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic inline __u64 uptr_to_u64(void __user *ptr)
19119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
19219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return (__u64)(unsigned long)ptr;
19319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
19419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
19519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_open(struct inode *inode, struct file *file)
19619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
19719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device;
19819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client;
19919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
20096b19062e741b715cf399312c30e0672d8889569Stefan Richter	device = fw_device_get_by_devt(inode->i_rdev);
201a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg	if (device == NULL)
202a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg		return -ENODEV;
20319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
204551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(device)) {
205551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		fw_device_put(device);
206551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
207551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	}
208551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
2092d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	client = kzalloc(sizeof(*client), GFP_KERNEL);
21096b19062e741b715cf399312c30e0672d8889569Stefan Richter	if (client == NULL) {
21196b19062e741b715cf399312c30e0672d8889569Stefan Richter		fw_device_put(device);
21219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
21396b19062e741b715cf399312c30e0672d8889569Stefan Richter	}
21419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
21596b19062e741b715cf399312c30e0672d8889569Stefan Richter	client->device = device;
21619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_lock_init(&client->lock);
21745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_init(&client->resource_idr);
21845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	INIT_LIST_HEAD(&client->event_list);
21919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	init_waitqueue_head(&client->wait);
220fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kref_init(&client->kref);
22119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
22219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	file->private_data = client;
22319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
224d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_lock(&device->client_list_mutex);
22597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	list_add_tail(&client->link, &device->client_list);
226d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_unlock(&device->client_list_mutex);
22797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
22819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
22919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
23019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
23119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void queue_event(struct client *client, struct event *event,
23219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			void *data0, size_t size0, void *data1, size_t size1)
23319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
23419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	unsigned long flags;
23519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
23619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[0].data = data0;
23719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[0].size = size0;
23819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[1].data = data1;
23919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[1].size = size1;
24019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
24119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
24245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (client->in_shutdown)
24345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		kfree(event);
24445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	else
24545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		list_add_tail(&event->link, &client->event_list);
24619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
24783431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason
24883431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason	wake_up_interruptible(&client->wait);
24919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
25019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
25153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dequeue_event(struct client *client,
25253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			 char __user *buffer, size_t count)
25319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
25419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event *event;
25519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	size_t size, total;
2562dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	int i, ret;
25719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2582dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = wait_event_interruptible(client->wait,
2592dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter			!list_empty(&client->event_list) ||
2602dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter			fw_device_is_shutdown(client->device));
2612dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
2622dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter		return ret;
26319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2642603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (list_empty(&client->event_list) &&
2652603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		       fw_device_is_shutdown(client->device))
2662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		return -ENODEV;
26719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2683ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&client->lock);
269a459b8ab9c176143fecef8ace4b70d6dbd7a8113Stefan Richter	event = list_first_entry(&client->event_list, struct event, link);
27019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	list_del(&event->link);
2713ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&client->lock);
27219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
27319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	total = 0;
27419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
27519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		size = min(event->v[i].size, count - total);
2762603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		if (copy_to_user(buffer + total, event->v[i].data, size)) {
2772dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter			ret = -EFAULT;
27819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			goto out;
2792603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		}
28019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		total += size;
28119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
2822dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = total;
28319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
28419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg out:
28519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(event);
28619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2872dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	return ret;
28819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
28919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
29053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic ssize_t fw_device_op_read(struct file *file, char __user *buffer,
29153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				 size_t count, loff_t *offset)
29219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
29319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
29419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
29519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dequeue_event(client, buffer, count);
29619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
29719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
29853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
29953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				 struct client *client)
300344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg{
301da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	struct fw_card *card = client->device->card;
302cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason
3033ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&card->lock);
304344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
305da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	event->closure	     = client->bus_reset_closure;
306344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->type          = FW_CDEV_EVENT_BUS_RESET;
307cf5a56ac8083dd04ffe8b9b2ec7895e9bcff44bcStefan Richter	event->generation    = client->device->generation;
308da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	event->node_id       = client->device->node_id;
309344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->local_node_id = card->local_node->node_id;
310344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->bm_node_id    = 0; /* FIXME: We don't track the BM. */
311344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->irm_node_id   = card->irm_node->node_id;
312344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->root_node_id  = card->root_node->node_id;
313cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason
3143ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&card->lock);
315344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg}
316344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
31753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void for_each_client(struct fw_device *device,
31853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			    void (*callback)(struct client *client))
3192603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
3202603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	struct client *c;
3212603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
322d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_lock(&device->client_list_mutex);
3232603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	list_for_each_entry(c, &device->client_list, link)
3242603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		callback(c);
325d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_unlock(&device->client_list_mutex);
3262603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
3272603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
328b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic int schedule_reallocations(int id, void *p, void *data)
329b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
3309fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	schedule_if_iso_resource(p);
331b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
332b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	return 0;
333b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
334b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
33553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void queue_bus_reset_event(struct client *client)
33697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{
33797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct bus_reset_event *e;
33897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
33997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e = kzalloc(sizeof(*e), GFP_KERNEL);
34097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (e == NULL) {
34197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg		fw_notify("Out of memory when allocating bus reset event\n");
34297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg		return;
34397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	}
34497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
34597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fill_bus_reset_event(&e->reset, client);
34697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
34797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	queue_event(client, &e->event,
34897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		    &e->reset, sizeof(e->reset), NULL, 0);
349b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
350b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
351b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	idr_for_each(&client->resource_idr, schedule_reallocations, client);
352b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
35397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg}
35497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
35597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergvoid fw_device_cdev_update(struct fw_device *device)
35697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{
3572603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	for_each_client(device, queue_bus_reset_event);
3582603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
35997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
3602603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void wake_up_client(struct client *client)
3612603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
3622603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	wake_up_interruptible(&client->wait);
3632603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
36497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
3652603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergvoid fw_device_cdev_remove(struct fw_device *device)
3662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
3672603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	for_each_client(device, wake_up_client);
36897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg}
36997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
3704f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_get_info(struct client *client, void *buffer)
37119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
3724f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_get_info *get_info = buffer;
373344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	struct fw_cdev_event_bus_reset bus_reset;
374c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	unsigned long ret = 0;
375344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
3764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	client->version = get_info->version;
3774f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	get_info->version = FW_CDEV_VERSION;
378cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason	get_info->card = client->device->card->index;
379344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
380c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	down_read(&fw_device_rwsem);
381c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
3824f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (get_info->rom != 0) {
3834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		void __user *uptr = u64_to_uptr(get_info->rom);
3844f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		size_t want = get_info->rom_length;
385d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter		size_t have = client->device->config_rom_length * 4;
386344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
387c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter		ret = copy_to_user(uptr, client->device->config_rom,
388c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter				   min(want, have));
389344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	}
3904f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	get_info->rom_length = client->device->config_rom_length * 4;
391344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
392c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	up_read(&fw_device_rwsem);
393c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
394c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	if (ret != 0)
395c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter		return -EFAULT;
396c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
3974f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	client->bus_reset_closure = get_info->bus_reset_closure;
3984f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (get_info->bus_reset != 0) {
3994f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		void __user *uptr = u64_to_uptr(get_info->bus_reset);
400344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
401da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg		fill_bus_reset_event(&bus_reset, client);
4022d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset)))
403344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg			return -EFAULT;
404344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	}
40519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
40619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
40719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
40819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
40953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int add_client_resource(struct client *client,
41053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			       struct client_resource *resource, gfp_t gfp_mask)
4113964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
4123964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	unsigned long flags;
41345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
41445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
41545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason retry:
41645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (idr_pre_get(&client->resource_idr, gfp_mask) == 0)
41745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		return -ENOMEM;
4183964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
4193964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
42045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (client->in_shutdown)
42145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		ret = -ECANCELED;
42245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	else
42345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		ret = idr_get_new(&client->resource_idr, resource,
42445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				  &resource->handle);
425b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (ret >= 0) {
426fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter		client_get(client);
4279fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_if_iso_resource(resource);
428b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
4293964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
43045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
43145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret == -EAGAIN)
43245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto retry;
43345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
43445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return ret < 0 ? ret : 0;
4353964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
4363964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
43753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int release_client_resource(struct client *client, u32 handle,
43853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				   client_resource_release_fn_t release,
439e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter				   struct client_resource **return_resource)
4403964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
441e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct client_resource *resource;
4423964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
4433ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&client->lock);
44445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (client->in_shutdown)
445e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		resource = NULL;
44645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	else
447e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		resource = idr_find(&client->resource_idr, handle);
448e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	if (resource && resource->release == release)
44945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		idr_remove(&client->resource_idr, handle);
4503ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&client->lock);
4513964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
452e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	if (!(resource && resource->release == release))
4533964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		return -EINVAL;
4543964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
455e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	if (return_resource)
456e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		*return_resource = resource;
4573964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	else
458e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		resource->release(client, resource);
4593964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
460fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
461fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
4623964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	return 0;
4633964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
4643964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
46553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_transaction(struct client *client,
46653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				struct client_resource *resource)
4673964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
46897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_resource *r = container_of(resource,
46997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			struct outbound_transaction_resource, resource);
4703964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
47197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fw_cancel_transaction(client->device->card, &r->transaction);
4723964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
4733964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
47453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void complete_transaction(struct fw_card *card, int rcode,
47553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				 void *payload, size_t length, void *data)
47619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
47797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_event *e = data;
47897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_response *rsp = &e->response;
47997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client *client = e->client;
48028cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	unsigned long flags;
48119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
48297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (length < rsp->length)
48397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		rsp->length = length;
48419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (rcode == RCODE_COMPLETE)
48597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		memcpy(rsp->data, payload, rsp->length);
48619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
48728cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
48845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	/*
489fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 * 1. If called while in shutdown, the idr tree must be left untouched.
490fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    The idr handle will be removed and the client reference will be
491fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    dropped later.
492fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 * 2. If the call chain was release_client_resource ->
493fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    release_transaction -> complete_transaction (instead of a normal
494fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    conclusion of the transaction), i.e. if this resource was already
495fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    unregistered from the idr, the client reference will be dropped
496fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    by release_client_resource and we must not drop it here.
49745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	 */
498fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	if (!client->in_shutdown &&
49997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	    idr_find(&client->resource_idr, e->r.resource.handle)) {
50097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		idr_remove(&client->resource_idr, e->r.resource.handle);
501fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter		/* Drop the idr's reference */
502fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter		client_put(client);
503fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	}
50428cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
50528cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
50697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	rsp->type = FW_CDEV_EVENT_RESPONSE;
50797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	rsp->rcode = rcode;
5088401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore
5098401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	/*
51097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	 * In the case that sizeof(*rsp) doesn't align with the position of the
5118401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * data, and the read is short, preserve an extra copy of the data
5128401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * to stay compatible with a pre-2.6.27 bug.  Since the bug is harmless
5138401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * for short reads and some apps depended on it, this is both safe
5148401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * and prudent for compatibility.
5158401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 */
51697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data))
51797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		queue_event(client, &e->event, rsp, sizeof(*rsp),
51897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			    rsp->data, rsp->length);
5198401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	else
52097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length,
5218401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore			    NULL, 0);
522fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
523fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	/* Drop the transaction callback's reference */
524fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
52519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
52619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
527acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int init_request(struct client *client,
528acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter			struct fw_cdev_send_request *request,
529acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter			int destination_id, int speed)
53019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
53197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_event *e;
5321f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter	int ret;
53319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
53418e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	if (request->tcode != TCODE_STREAM_DATA &&
53518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	    (request->length > 4096 || request->length > 512 << speed))
5365d3fd692a7196a9045fb606f891f5987959b65a0Stefan Richter		return -EIO;
53719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
53897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
53997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (e == NULL)
54019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
54119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
54297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->client = client;
54397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->response.length = request->length;
54497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->response.closure = request->closure;
54519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5464f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->data &&
54797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	    copy_from_user(e->response.data,
5484f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			   u64_to_uptr(request->data), request->length)) {
5491f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter		ret = -EFAULT;
55045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
5511f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter	}
5521f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter
55397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->r.resource.release = release_transaction;
55497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
55545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0)
55645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
55728cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
558fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	/* Get a reference for the transaction callback */
559fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_get(client);
560fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
561acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	fw_send_request(client->device->card, &e->r.transaction,
562664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter			request->tcode, destination_id, request->generation,
563664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter			speed, request->offset, e->response.data,
564664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter			request->length, complete_transaction, e);
565664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter	return 0;
56619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
56745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed:
56897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(e);
5691f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter
5701f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter	return ret;
57119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
57219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
573acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int ioctl_send_request(struct client *client, void *buffer)
574acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{
575acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	struct fw_cdev_send_request *request = buffer;
576acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
577acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	switch (request->tcode) {
578acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_QUADLET_REQUEST:
579acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_BLOCK_REQUEST:
580acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_READ_QUADLET_REQUEST:
581acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_READ_BLOCK_REQUEST:
582acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_MASK_SWAP:
583acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_COMPARE_SWAP:
584acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_FETCH_ADD:
585acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_LITTLE_ADD:
586acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_BOUNDED_ADD:
587acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_WRAP_ADD:
588acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_VENDOR_DEPENDENT:
589acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		break;
590acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	default:
591acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		return -EINVAL;
592acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	}
593acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
594207fbefb18de9bc6f871e4008da29879c90cb67eStefan Richter	return init_request(client, request, client->device->node_id,
595acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter			    client->device->max_speed);
596acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter}
597acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
59853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_request(struct client *client,
59953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			    struct client_resource *resource)
6003964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
60197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_resource *r = container_of(resource,
60297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			struct inbound_transaction_resource, resource);
6033964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
604db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch	if (r->request)
605db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch		fw_send_response(client->device->card, r->request,
606db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch				 RCODE_CONFLICT_ERROR);
60797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
6083964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
6093964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
61097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstatic void handle_request(struct fw_card *card, struct fw_request *request,
61153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			   int tcode, int destination, int source,
61253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			   int generation, int speed,
61353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			   unsigned long long offset,
61453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			   void *payload, size_t length, void *callback_data)
61519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
61697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct address_handler_resource *handler = callback_data;
61797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_resource *r;
61897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_event *e;
61945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
62019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
62197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r = kmalloc(sizeof(*r), GFP_ATOMIC);
6222d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	e = kmalloc(sizeof(*e), GFP_ATOMIC);
62397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (r == NULL || e == NULL)
62445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
62519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
62697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->request = request;
62797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->data    = payload;
62897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->length  = length;
62919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
63097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->resource.release = release_request;
63197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC);
63245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0)
63345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
63419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
63519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.type    = FW_CDEV_EVENT_REQUEST;
63619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.tcode   = tcode;
63719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.offset  = offset;
63819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.length  = length;
63997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->request.handle  = r->resource.handle;
64019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.closure = handler->closure;
64119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
64297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	queue_event(handler->client, &e->event,
6432d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		    &e->request, sizeof(e->request), payload, length);
64445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return;
64545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
64645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed:
64797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
64845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	kfree(e);
649db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch	if (request)
650db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch		fw_send_response(card, request, RCODE_CONFLICT_ERROR);
65119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
65219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
65353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_address_handler(struct client *client,
65453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				    struct client_resource *resource)
6553964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
65697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct address_handler_resource *r =
65797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	    container_of(resource, struct address_handler_resource, resource);
6583964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
65997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fw_core_remove_address_handler(&r->handler);
66097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
6613964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
6623964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
6634f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_allocate(struct client *client, void *buffer)
66419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
6654f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_allocate *request = buffer;
66697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct address_handler_resource *r;
66719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_address_region region;
66845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
66919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
67097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r = kmalloc(sizeof(*r), GFP_KERNEL);
67197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (r == NULL)
67219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
67319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6744f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	region.start = request->offset;
6754f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	region.end = request->offset + request->length;
67697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->handler.length = request->length;
67797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->handler.address_callback = handle_request;
67897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->handler.callback_data = r;
67997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->closure = request->closure;
68097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->client = client;
68119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
68297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = fw_core_add_address_handler(&r->handler, &region);
6833e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter	if (ret < 0) {
68497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		kfree(r);
6853e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter		return ret;
68619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
68719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
68897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->resource.release = release_address_handler;
68997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(client, &r->resource, GFP_KERNEL);
69045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0) {
69197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		release_address_handler(client, &r->resource);
69245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		return ret;
69345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	}
69497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	request->handle = r->resource.handle;
69519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
69619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
69719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
69819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6994f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_deallocate(struct client *client, void *buffer)
7009472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg{
7014f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_deallocate *request = buffer;
7029472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg
70345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return release_client_resource(client, request->handle,
70445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				       release_address_handler, NULL);
7059472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg}
7069472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg
7074f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_send_response(struct client *client, void *buffer)
70819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
7094f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_send_response *request = buffer;
7103964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource *resource;
71197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_resource *r;
7127e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter	int ret = 0;
71319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
71445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (release_client_resource(client, request->handle,
71545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				    release_request, &resource) < 0)
71619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
71745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
71897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r = container_of(resource, struct inbound_transaction_resource,
71997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			 resource);
720db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch	if (r->request) {
721db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch		if (request->length < r->length)
722db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch			r->length = request->length;
723db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch		if (copy_from_user(r->data, u64_to_uptr(request->data),
724db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch				   r->length)) {
725db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch			ret = -EFAULT;
726cf0e575dcc4cab9fd955e9bec49df7e8ee30a7cfStefan Richter			kfree(r->request);
727db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch			goto out;
728db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch		}
729db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch		fw_send_response(client->device->card, r->request,
730db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch				 request->rcode);
7317e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter	}
7327e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter out:
73319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(r);
73419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7357e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter	return ret;
73619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
73719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7384f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_initiate_bus_reset(struct client *client, void *buffer)
7395371842b723dd04df57171f2c74660966901380cKristian Høgsberg{
7404f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_initiate_bus_reset *request = buffer;
7415371842b723dd04df57171f2c74660966901380cKristian Høgsberg	int short_reset;
7425371842b723dd04df57171f2c74660966901380cKristian Høgsberg
7434f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	short_reset = (request->type == FW_CDEV_SHORT_RESET);
7445371842b723dd04df57171f2c74660966901380cKristian Høgsberg
7455371842b723dd04df57171f2c74660966901380cKristian Høgsberg	return fw_core_initiate_bus_reset(client->device->card, short_reset);
7465371842b723dd04df57171f2c74660966901380cKristian Høgsberg}
7475371842b723dd04df57171f2c74660966901380cKristian Høgsberg
7483964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void release_descriptor(struct client *client,
7493964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			       struct client_resource *resource)
7503964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
75197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct descriptor_resource *r =
75297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		container_of(resource, struct descriptor_resource, resource);
7533964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
75497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fw_core_remove_descriptor(&r->descriptor);
75597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
7563964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
7573964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
7584f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_add_descriptor(struct client *client, void *buffer)
75966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{
7604f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_add_descriptor *request = buffer;
76197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct descriptor_resource *r;
76245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
76366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
764de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter	/* Access policy: Allow this ioctl only on local nodes' device files. */
76592368890d551794ee8d7e90477d8498bb7f82a9bStefan Richter	if (!client->device->is_local)
766de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter		return -ENOSYS;
767de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter
7684f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->length > 256)
76966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -EINVAL;
77066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
77197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r = kmalloc(sizeof(*r) + request->length * 4, GFP_KERNEL);
77297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (r == NULL)
77366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -ENOMEM;
77466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
77597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (copy_from_user(r->data,
7764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			   u64_to_uptr(request->data), request->length * 4)) {
77745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		ret = -EFAULT;
77845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
77966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	}
78066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
78197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->descriptor.length    = request->length;
78297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->descriptor.immediate = request->immediate;
78397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->descriptor.key       = request->key;
78497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->descriptor.data      = r->data;
78566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
78697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = fw_core_add_descriptor(&r->descriptor);
78745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0)
78845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
78966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
79097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->resource.release = release_descriptor;
79197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(client, &r->resource, GFP_KERNEL);
79245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0) {
79397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		fw_core_remove_descriptor(&r->descriptor);
79445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
79545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	}
79697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	request->handle = r->resource.handle;
79766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
79866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	return 0;
79945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed:
80097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
80145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
80245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return ret;
80366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg}
80466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
8054f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_remove_descriptor(struct client *client, void *buffer)
80666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{
8074f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_remove_descriptor *request = buffer;
80866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
80945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return release_client_resource(client, request->handle,
81045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				       release_descriptor, NULL);
81166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg}
81266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
81353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void iso_callback(struct fw_iso_context *context, u32 cycle,
81453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			 size_t header_length, void *header, void *data)
81519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
81619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = data;
81797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct iso_interrupt_event *e;
81819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
81997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e = kzalloc(sizeof(*e) + header_length, GFP_ATOMIC);
82097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (e == NULL)
82119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return;
82219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
82397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
82497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.closure   = client->iso_closure;
82597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.cycle     = cycle;
82697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.header_length = header_length;
82797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	memcpy(e->interrupt.header, header, header_length);
82897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	queue_event(client, &e->event, &e->interrupt,
82997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		    sizeof(e->interrupt) + header_length, NULL, 0);
83019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
83119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8324f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_create_iso_context(struct client *client, void *buffer)
83319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
8344f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_create_iso_context *request = buffer;
83524315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	struct fw_iso_context *context;
83619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
837fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter	/* We only support one context at this time. */
838fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter	if (client->iso_context != NULL)
839fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter		return -EBUSY;
840fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter
8414f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->channel > 63)
84221efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg		return -EINVAL;
84321efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg
8444f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	switch (request->type) {
845c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	case FW_ISO_CONTEXT_RECEIVE:
8464f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->header_size < 4 || (request->header_size & 3))
847c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg			return -EINVAL;
84898b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg
849c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg		break;
850c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
851c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	case FW_ISO_CONTEXT_TRANSMIT:
8524f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->speed > SCODE_3200)
853c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg			return -EINVAL;
854c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
855c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg		break;
856c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
857c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	default:
85821efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg		return -EINVAL;
859c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	}
860c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
86124315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	context =  fw_iso_context_create(client->device->card,
86224315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg					 request->type,
86324315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg					 request->channel,
86424315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg					 request->speed,
86524315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg					 request->header_size,
86624315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg					 iso_callback, client);
86724315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	if (IS_ERR(context))
86824315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg		return PTR_ERR(context);
86924315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg
870abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	client->iso_closure = request->closure;
87124315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	client->iso_context = context;
87219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
873abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	/* We only support one context at this time. */
874abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	request->handle = 0;
875abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
87619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
87719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
87819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8791ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg/* Macros for decoding the iso packet control header. */
8801ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_PAYLOAD_LENGTH(v)	((v) & 0xffff)
8811ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_INTERRUPT(v)	(((v) >> 16) & 0x01)
8821ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_SKIP(v)		(((v) >> 17) & 0x01)
8837a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_TAG(v)		(((v) >> 18) & 0x03)
8847a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_SY(v)		(((v) >> 20) & 0x0f)
8851ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_HEADER_LENGTH(v)	(((v) >> 24) & 0xff)
8861ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg
8874f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_queue_iso(struct client *client, void *buffer)
88819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
8894f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_queue_iso *request = buffer;
89019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_iso_packet __user *p, *end, *next;
8919b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg	struct fw_iso_context *ctx = client->iso_context;
892ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	unsigned long payload, buffer_end, header_length;
8931ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg	u32 control;
89419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	int count;
89519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct {
89619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		struct fw_iso_packet packet;
89719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		u8 header[256];
89819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	} u;
89919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
900abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	if (ctx == NULL || request->handle != 0)
90119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
90219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
903c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	/*
904c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * If the user passes a non-NULL data pointer, has mmap()'ed
90519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * the iso buffer, and the pointer points inside the buffer,
90619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * we setup the payload pointers accordingly.  Otherwise we
9079aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	 * set them both to 0, which will still let packets with
90819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * payload_length == 0 through.  In other words, if no packets
90919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * use the indirect payload, the iso buffer need not be mapped
910c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * and the request->data pointer is ignored.
911c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 */
91219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9134f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	payload = (unsigned long)request->data - client->vm_start;
914ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	buffer_end = client->buffer.page_count << PAGE_SHIFT;
9154f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->data == 0 || client->buffer.pages == NULL ||
916ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	    payload >= buffer_end) {
9179aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		payload = 0;
918ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg		buffer_end = 0;
91919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
92019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9211ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro	p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets);
9221ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro
9231ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro	if (!access_ok(VERIFY_READ, p, request->size))
92419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EFAULT;
92519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9264f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	end = (void __user *)p + request->size;
92719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	count = 0;
92819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	while (p < end) {
9291ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		if (get_user(control, &p->control))
93019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EFAULT;
9311ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.payload_length = GET_PAYLOAD_LENGTH(control);
9321ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.interrupt = GET_INTERRUPT(control);
9331ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.skip = GET_SKIP(control);
9341ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.tag = GET_TAG(control);
9351ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.sy = GET_SY(control);
9361ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.header_length = GET_HEADER_LENGTH(control);
937295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg
9389b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg		if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) {
939295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			header_length = u.packet.header_length;
940295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		} else {
941c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			/*
942c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 * We require that header_length is a multiple of
943c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 * the fixed header size, ctx->header_size.
944c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 */
9459b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			if (ctx->header_size == 0) {
9469b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg				if (u.packet.header_length > 0)
9479b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg					return -EINVAL;
9489b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			} else if (u.packet.header_length % ctx->header_size != 0) {
949295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg				return -EINVAL;
9509b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			}
951295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			header_length = 0;
952295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		}
953295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg
95419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		next = (struct fw_cdev_iso_packet __user *)
955295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			&p->header[header_length / 4];
95619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		if (next > end)
95719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
95819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		if (__copy_from_user
959295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		    (u.packet.header, p->header, header_length))
96019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EFAULT;
96198b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg		if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
96219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		    u.packet.header_length + u.packet.payload_length > 0)
96319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
964ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg		if (payload + u.packet.payload_length > buffer_end)
96519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
96619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9679b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg		if (fw_iso_context_queue(ctx, &u.packet,
9689b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg					 &client->buffer, payload))
96919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			break;
97019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
97119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		p = next;
97219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		payload += u.packet.payload_length;
97319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		count++;
97419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
97519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9764f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->size    -= uptr_to_u64(p) - request->packets;
9774f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->packets  = uptr_to_u64(p);
9784f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	request->data     = client->vm_start + payload;
97919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
98019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return count;
98119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
98219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_start_iso(struct client *client, void *buffer)
98419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
9854f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	struct fw_cdev_start_iso *request = buffer;
98619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
987fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter	if (client->iso_context == NULL || request->handle != 0)
988abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg		return -EINVAL;
989fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter
990eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg	if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) {
9914f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->tags == 0 || request->tags > 15)
992eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg			return -EINVAL;
993eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg
9944f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		if (request->sync > 15)
995eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg			return -EINVAL;
996eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg	}
997eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg
9984f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	return fw_iso_context_start(client->iso_context, request->cycle,
9994f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg				    request->sync, request->tags);
100019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
100119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int ioctl_stop_iso(struct client *client, void *buffer)
1003b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg{
1004abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	struct fw_cdev_stop_iso *request = buffer;
1005abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
1006fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter	if (client->iso_context == NULL || request->handle != 0)
1007abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg		return -EINVAL;
1008abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
1009b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg	return fw_iso_context_stop(client->iso_context);
1010b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg}
1011b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg
1012a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richterstatic int ioctl_get_cycle_timer(struct client *client, void *buffer)
1013a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter{
1014a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	struct fw_cdev_get_cycle_timer *request = buffer;
1015a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	struct fw_card *card = client->device->card;
1016a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	struct timeval tv;
10174a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter	u32 cycle_time;
1018a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
10194a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter	local_irq_disable();
1020a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
1021168cf9af699e87d5a6f44b684583714ecabb8e71Stefan Richter	cycle_time = card->driver->get_cycle_time(card);
1022a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	do_gettimeofday(&tv);
1023a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
10244a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter	local_irq_enable();
1025a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
1026a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	request->local_time = tv.tv_sec * 1000000ULL + tv.tv_usec;
10274a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter	request->cycle_timer = cycle_time;
10284a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter
1029a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	return 0;
1030a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter}
1031a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
1032b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void iso_resource_work(struct work_struct *work)
1033b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
1034b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource_event *e;
1035b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource *r =
1036b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter			container_of(work, struct iso_resource, work.work);
1037b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct client *client = r->client;
1038b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	int generation, channel, bandwidth, todo;
1039b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	bool skip, free, success;
1040b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1041b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
1042b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	generation = client->device->generation;
1043b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	todo = r->todo;
1044b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/* Allow 1000ms grace period for other reallocations. */
1045b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (todo == ISO_RES_ALLOC &&
1046b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) {
10479fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3));
1048b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		skip = true;
1049b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	} else {
1050b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		/* We could be called twice within the same generation. */
1051b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		skip = todo == ISO_RES_REALLOC &&
1052b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		       r->generation == generation;
1053b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
10541ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	free = todo == ISO_RES_DEALLOC ||
10551ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	       todo == ISO_RES_ALLOC_ONCE ||
10561ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	       todo == ISO_RES_DEALLOC_ONCE;
1057b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->generation = generation;
1058b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
1059b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1060b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (skip)
1061b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto out;
1062b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1063b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	bandwidth = r->bandwidth;
1064b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1065b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	fw_iso_resource_manage(client->device->card, generation,
1066b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter			r->channels, &channel, &bandwidth,
10671ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter			todo == ISO_RES_ALLOC ||
10681ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter			todo == ISO_RES_REALLOC ||
10696fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter			todo == ISO_RES_ALLOC_ONCE,
10706fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter			r->transaction_data);
1071b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/*
1072b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * Is this generation outdated already?  As long as this resource sticks
1073b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * in the idr, it will be scheduled again for a newer generation or at
1074b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * shutdown.
1075b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 */
1076b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (channel == -EAGAIN &&
1077b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC))
1078b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto out;
1079b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1080b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	success = channel >= 0 || bandwidth > 0;
1081b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1082b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
1083b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/*
1084b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * Transit from allocation to reallocation, except if the client
1085b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * requested deallocation in the meantime.
1086b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 */
1087b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (r->todo == ISO_RES_ALLOC)
1088b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		r->todo = ISO_RES_REALLOC;
1089b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/*
1090b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * Allocation or reallocation failure?  Pull this resource out of the
1091b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * idr and prepare for deletion, unless the client is shutting down.
1092b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 */
1093b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (r->todo == ISO_RES_REALLOC && !success &&
1094b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    !client->in_shutdown &&
1095b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    idr_find(&client->resource_idr, r->resource.handle)) {
1096b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		idr_remove(&client->resource_idr, r->resource.handle);
1097b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		client_put(client);
1098b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		free = true;
1099b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1100b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
1101b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1102b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (todo == ISO_RES_ALLOC && channel >= 0)
11035d9cb7d276a9c465fef5a771792eac2cf1929f2bStefan Richter		r->channels = 1ULL << channel;
1104b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1105b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (todo == ISO_RES_REALLOC && success)
1106b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto out;
1107b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
11081ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
1109b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		e = r->e_alloc;
1110b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		r->e_alloc = NULL;
1111b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	} else {
1112b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		e = r->e_dealloc;
1113b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		r->e_dealloc = NULL;
1114b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1115e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e->iso_resource.handle    = r->resource.handle;
1116e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e->iso_resource.channel   = channel;
1117e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e->iso_resource.bandwidth = bandwidth;
1118b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1119b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	queue_event(client, &e->event,
1120e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		    &e->iso_resource, sizeof(e->iso_resource), NULL, 0);
1121b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1122b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (free) {
1123b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		cancel_delayed_work(&r->work);
1124b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		kfree(r->e_alloc);
1125b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		kfree(r->e_dealloc);
1126b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		kfree(r);
1127b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1128b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter out:
1129b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	client_put(client);
1130b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1131b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1132b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *client,
1133b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter				 struct client_resource *resource)
1134b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
1135b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource *r =
1136b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		container_of(resource, struct iso_resource, resource);
1137b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1138b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
1139b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->todo = ISO_RES_DEALLOC;
11409fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	schedule_iso_resource(r, 0);
1141b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
1142b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1143b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
11441ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int init_iso_resource(struct client *client,
11451ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		struct fw_cdev_allocate_iso_resource *request, int todo)
1146b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
1147b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource_event *e1, *e2;
1148b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource *r;
1149b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	int ret;
1150b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1151b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if ((request->channels == 0 && request->bandwidth == 0) ||
1152b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL ||
1153b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    request->bandwidth < 0)
1154b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		return -EINVAL;
1155b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1156b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r  = kmalloc(sizeof(*r), GFP_KERNEL);
1157b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	e1 = kmalloc(sizeof(*e1), GFP_KERNEL);
1158b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	e2 = kmalloc(sizeof(*e2), GFP_KERNEL);
1159b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (r == NULL || e1 == NULL || e2 == NULL) {
1160b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		ret = -ENOMEM;
1161b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto fail;
1162b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1163b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1164b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	INIT_DELAYED_WORK(&r->work, iso_resource_work);
1165b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->client	= client;
11661ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	r->todo		= todo;
1167b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->generation	= -1;
1168b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->channels	= request->channels;
1169b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->bandwidth	= request->bandwidth;
1170b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->e_alloc	= e1;
1171b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->e_dealloc	= e2;
1172b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1173e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e1->iso_resource.closure = request->closure;
1174e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e1->iso_resource.type    = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED;
1175e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e2->iso_resource.closure = request->closure;
1176e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e2->iso_resource.type    = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
1177b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
11781ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	if (todo == ISO_RES_ALLOC) {
11791ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		r->resource.release = release_iso_resource;
11801ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		ret = add_client_resource(client, &r->resource, GFP_KERNEL);
118181610b8fbfc027a67707ff567d490819a3d55844Stefan Richter		if (ret < 0)
118281610b8fbfc027a67707ff567d490819a3d55844Stefan Richter			goto fail;
11831ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	} else {
11841ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		r->resource.release = NULL;
11851ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		r->resource.handle = -1;
11869fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_iso_resource(r, 0);
11871ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	}
1188b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	request->handle = r->resource.handle;
1189b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1190b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	return 0;
1191b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fail:
1192b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	kfree(r);
1193b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	kfree(e1);
1194b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	kfree(e2);
1195b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1196b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	return ret;
1197b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1198b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
11991ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int ioctl_allocate_iso_resource(struct client *client, void *buffer)
12001ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{
12011ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	struct fw_cdev_allocate_iso_resource *request = buffer;
12021ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
12031ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	return init_iso_resource(client, request, ISO_RES_ALLOC);
12041ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter}
12051ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
1206b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic int ioctl_deallocate_iso_resource(struct client *client, void *buffer)
1207b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
1208b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct fw_cdev_deallocate *request = buffer;
1209b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1210b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	return release_client_resource(client, request->handle,
1211b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter				       release_iso_resource, NULL);
1212b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1213b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
12141ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int ioctl_allocate_iso_resource_once(struct client *client, void *buffer)
12151ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{
12161ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	struct fw_cdev_allocate_iso_resource *request = buffer;
12171ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
12181ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	return init_iso_resource(client, request, ISO_RES_ALLOC_ONCE);
12191ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter}
12201ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
12211ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int ioctl_deallocate_iso_resource_once(struct client *client, void *buffer)
12221ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{
12231ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	struct fw_cdev_allocate_iso_resource *request = buffer;
12241ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
12251ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE);
12261ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter}
12271ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
1228c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter/*
1229c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * Returns a speed code:  Maximum speed to or from this device,
1230c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * limited by the device's link speed, the local node's link speed,
1231c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * and all PHY port speeds between the two links.
1232c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter */
123333580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richterstatic int ioctl_get_speed(struct client *client, void *buffer)
123433580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter{
1235c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter	return client->device->max_speed;
123633580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter}
123733580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter
1238acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int ioctl_send_broadcast_request(struct client *client, void *buffer)
1239acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{
1240acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	struct fw_cdev_send_request *request = buffer;
1241acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
1242acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	switch (request->tcode) {
1243acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_QUADLET_REQUEST:
1244acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_BLOCK_REQUEST:
1245acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		break;
1246acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	default:
1247acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		return -EINVAL;
1248acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	}
1249acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
12501566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter	/* Security policy: Only allow accesses to Units Space. */
12511566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter	if (request->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END)
12521566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter		return -EACCES;
12531566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter
1254acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100);
1255acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter}
1256acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
1257f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlasonstatic int ioctl_send_stream_packet(struct client *client, void *buffer)
1258f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason{
125918e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	struct fw_cdev_send_stream_packet *p = buffer;
126018e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	struct fw_cdev_send_request request;
126118e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	int dest;
1262f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason
126318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	if (p->speed > client->device->card->link_speed ||
126418e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	    p->length > 1024 << p->speed)
126518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter		return -EIO;
1266f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason
126718e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	if (p->tag > 3 || p->channel > 63 || p->sy > 15)
126818e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter		return -EINVAL;
126918e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter
127018e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	dest = fw_stream_packet_destination_id(p->tag, p->channel, p->sy);
127118e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	request.tcode		= TCODE_STREAM_DATA;
127218e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	request.length		= p->length;
127318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	request.closure		= p->closure;
127418e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	request.data		= p->data;
127518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	request.generation	= p->generation;
127618e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter
127718e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	return init_request(client, &request, dest, p->speed);
1278f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason}
1279f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason
12804f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsbergstatic int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
12814f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_get_info,
12824f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_send_request,
12834f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_allocate,
12844f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_deallocate,
12854f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_send_response,
12864f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_initiate_bus_reset,
12874f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_add_descriptor,
12884f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_remove_descriptor,
12894f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_create_iso_context,
12904f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_queue_iso,
12914f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_start_iso,
12924f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_stop_iso,
1293a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	ioctl_get_cycle_timer,
1294b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	ioctl_allocate_iso_resource,
1295b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	ioctl_deallocate_iso_resource,
12961ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	ioctl_allocate_iso_resource_once,
12971ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	ioctl_deallocate_iso_resource_once,
129833580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter	ioctl_get_speed,
1299acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	ioctl_send_broadcast_request,
1300f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason	ioctl_send_stream_packet,
13014f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg};
13024f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
130353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dispatch_ioctl(struct client *client,
130453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			  unsigned int cmd, void __user *arg)
130519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
1306b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter	char buffer[sizeof(union {
1307b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_get_info			_00;
1308b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_send_request		_01;
1309b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_allocate			_02;
1310b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_deallocate		_03;
1311b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_send_response		_04;
1312b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_initiate_bus_reset	_05;
1313b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_add_descriptor		_06;
1314b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_remove_descriptor	_07;
1315b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_create_iso_context	_08;
1316b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_queue_iso		_09;
1317b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_start_iso		_0a;
1318b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_stop_iso			_0b;
1319b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_get_cycle_timer		_0c;
1320b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_allocate_iso_resource	_0d;
1321b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter		struct fw_cdev_send_stream_packet	_13;
1322b2c0a2ac3e59f4764c59c23bd90b571e44256030Stefan Richter	})];
13232dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	int ret;
13244f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
13254f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_TYPE(cmd) != '#' ||
13264f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers))
132719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
13284f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
13294f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_DIR(cmd) & _IOC_WRITE) {
13302d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (_IOC_SIZE(cmd) > sizeof(buffer) ||
13314f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		    copy_from_user(buffer, arg, _IOC_SIZE(cmd)))
13324f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			return -EFAULT;
13334f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	}
13344f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
13352dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
13362dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
13372dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter		return ret;
13384f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
13394f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_DIR(cmd) & _IOC_READ) {
13402d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (_IOC_SIZE(cmd) > sizeof(buffer) ||
13414f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg		    copy_to_user(arg, buffer, _IOC_SIZE(cmd)))
13424f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			return -EFAULT;
134319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
13444f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
13452dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	return ret;
134619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
134719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
134853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_ioctl(struct file *file,
134953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			       unsigned int cmd, unsigned long arg)
135019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
135119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
135219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1353551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(client->device))
1354551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
1355551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
135619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dispatch_ioctl(client, cmd, (void __user *) arg);
135719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
135819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
135919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT
136053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_compat_ioctl(struct file *file,
136153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				      unsigned int cmd, unsigned long arg)
136219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
136319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
136419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1365551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(client->device))
1366551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
1367551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
136819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dispatch_ioctl(client, cmd, compat_ptr(arg));
136919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
137019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif
137119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
137219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
137319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
137419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
13759aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	enum dma_data_direction direction;
13769aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	unsigned long size;
13772dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	int page_count, ret;
13789aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
1379551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(client->device))
1380551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
1381551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
13829aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	/* FIXME: We could support multiple buffers, but we don't. */
13839aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (client->buffer.pages != NULL)
13849aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EBUSY;
13859aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
13869aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (!(vma->vm_flags & VM_SHARED))
13879aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EINVAL;
138819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
13899aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (vma->vm_start & ~PAGE_MASK)
139019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
139119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
139219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	client->vm_start = vma->vm_start;
13939aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	size = vma->vm_end - vma->vm_start;
13949aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	page_count = size >> PAGE_SHIFT;
13959aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (size & ~PAGE_MASK)
13969aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EINVAL;
13979aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
13989aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (vma->vm_flags & VM_WRITE)
13999aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		direction = DMA_TO_DEVICE;
14009aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	else
14019aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		direction = DMA_FROM_DEVICE;
14029aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
14032dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = fw_iso_buffer_init(&client->buffer, client->device->card,
14042dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter				 page_count, direction);
14052dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
14062dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter		return ret;
140719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
14082dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = fw_iso_buffer_map(&client->buffer, vma);
14092dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
14109aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		fw_iso_buffer_destroy(&client->buffer, client->device->card);
14119aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
14122dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	return ret;
141319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
141419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
141545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlasonstatic int shutdown_resource(int id, void *p, void *data)
141645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason{
1417e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct client_resource *resource = p;
141845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	struct client *client = data;
141945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
1420e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	resource->release(client, resource);
1421fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
142245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
142345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return 0;
142445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason}
142545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
142619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_release(struct inode *inode, struct file *file)
142719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
142819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
1429e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct event *event, *next_event;
143019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
143197811e347310766030a648fdf0e407b2c91a39c1Stefan Richter	mutex_lock(&client->device->client_list_mutex);
143297811e347310766030a648fdf0e407b2c91a39c1Stefan Richter	list_del(&client->link);
143397811e347310766030a648fdf0e407b2c91a39c1Stefan Richter	mutex_unlock(&client->device->client_list_mutex);
143497811e347310766030a648fdf0e407b2c91a39c1Stefan Richter
143519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (client->iso_context)
143619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		fw_iso_context_destroy(client->iso_context);
143719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
143836a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter	if (client->buffer.pages)
143936a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter		fw_iso_buffer_destroy(&client->buffer, client->device->card);
144036a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter
144145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	/* Freeze client->resource_idr and client->event_list */
14423ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&client->lock);
144345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	client->in_shutdown = true;
14443ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&client->lock);
144566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
144645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_for_each(&client->resource_idr, shutdown_resource, client);
144745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_remove_all(&client->resource_idr);
144845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_destroy(&client->resource_idr);
144928cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
1450e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	list_for_each_entry_safe(event, next_event, &client->event_list, link)
1451e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		kfree(event);
145219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1453fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
145419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
145519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
145619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
145719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
145819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic unsigned int fw_device_op_poll(struct file *file, poll_table * pt)
145919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
146019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
14612603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	unsigned int mask = 0;
146219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
146319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	poll_wait(file, &client->wait, pt);
146419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
14652603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (fw_device_is_shutdown(client->device))
14662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		mask |= POLLHUP | POLLERR;
146719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (!list_empty(&client->event_list))
14682603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		mask |= POLLIN | POLLRDNORM;
14692603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
14702603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	return mask;
147119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
147219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
147321ebcd1224d05c8673053e1e93ab9ec7ef3e0b84Stefan Richterconst struct file_operations fw_device_ops = {
147419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.owner		= THIS_MODULE,
147519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.open		= fw_device_op_open,
147619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.read		= fw_device_op_read,
147719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.unlocked_ioctl	= fw_device_op_ioctl,
147819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.poll		= fw_device_op_poll,
147919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.release	= fw_device_op_release,
148019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.mmap		= fw_device_op_mmap,
148119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
148219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT
14835af4e5eab30d481f76b89a2167c873dfad960acbStefan Richter	.compat_ioctl	= fw_device_op_compat_ioctl,
148419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif
148519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
1486