core-cdev.c revision 385ab5bcd4be586dffdba550b310308d89eade71
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>
38281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter#include <linux/string.h>
39be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/time.h>
40e034d242593f12533c11742ce38c245a33e57dc7Stefan Richter#include <linux/uaccess.h>
41be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/vmalloc.h>
42be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/wait.h>
43b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/workqueue.h>
44be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter
45a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter#include <asm/system.h>
46be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter
4777c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include "core.h"
4819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
4919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct client {
50344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	u32 version;
5119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device;
5245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
5319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spinlock_t lock;
5445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	bool in_shutdown;
5545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	struct idr resource_idr;
5619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct list_head event_list;
5719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	wait_queue_head_t wait;
58da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	u64 bus_reset_closure;
599aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
6019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_iso_context *iso_context;
61abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	u64 iso_closure;
629aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	struct fw_iso_buffer buffer;
639aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	unsigned long vm_start;
6497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
6597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct list_head link;
66fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	struct kref kref;
6719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
6819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
69fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic inline void client_get(struct client *client)
70fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{
71fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kref_get(&client->kref);
72fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter}
73fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
74fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_release(struct kref *kref)
75fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{
76fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	struct client *client = container_of(kref, struct client, kref);
77fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
78fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	fw_device_put(client->device);
79fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kfree(client);
80fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter}
81fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
82fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_put(struct client *client)
83fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{
84fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kref_put(&client->kref, client_release);
85fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter}
86fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
8797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource;
8897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richtertypedef void (*client_resource_release_fn_t)(struct client *,
8997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter					     struct client_resource *);
9097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource {
9197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	client_resource_release_fn_t release;
9297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	int handle;
9397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
9497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
9597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct address_handler_resource {
9697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
9797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_address_handler handler;
9897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	__u64 closure;
9997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client *client;
10097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
10197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
10297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_resource {
10397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
10497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_transaction transaction;
10597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
10697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
10797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_resource {
10897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
10997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_request *request;
11097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	void *data;
11197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	size_t length;
11297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
11397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
11497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct descriptor_resource {
11597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
11697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_descriptor descriptor;
11797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	u32 data[0];
11897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
11997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
120b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource {
121b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct client_resource resource;
122b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct client *client;
123b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/* Schedule work and access todo only with client->lock held. */
124b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct delayed_work work;
1251ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
1261ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	      ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
127b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	int generation;
128b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	u64 channels;
129b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	s32 bandwidth;
1306fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter	__be32 transaction_data[2];
131b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource_event *e_alloc, *e_dealloc;
132b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter};
133b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
134b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *, struct client_resource *);
135b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1369fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_iso_resource(struct iso_resource *r, unsigned long delay)
1379fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{
1389fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	client_get(r->client);
1399fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	if (!schedule_delayed_work(&r->work, delay))
1409fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		client_put(r->client);
1419fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter}
1429fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter
1439fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_if_iso_resource(struct client_resource *resource)
1449fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{
1459fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	if (resource->release == release_iso_resource)
1469fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_iso_resource(container_of(resource,
1479fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter					struct iso_resource, resource), 0);
1489fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter}
1499fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter
15097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter/*
15197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * dequeue_event() just kfree()'s the event, so the event has to be
15297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * the first field in a struct XYZ_event.
15397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter */
15497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct event {
15597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct { void *data; size_t size; } v[2];
15697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct list_head link;
15797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
15897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
15997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct bus_reset_event {
16097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
16197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_bus_reset reset;
16297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
16397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
16497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_event {
16597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
16697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client *client;
16797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_resource r;
16897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_response response;
16997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
17097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
17197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_event {
17297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
17397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_request request;
17497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
17597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
17697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct iso_interrupt_event {
17797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
17897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_iso_interrupt interrupt;
17997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
18097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
181b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource_event {
182b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct event event;
183e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct fw_cdev_event_iso_resource iso_resource;
184b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter};
185b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
18653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic inline void __user *u64_to_uptr(__u64 value)
18719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
18819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return (void __user *)(unsigned long)value;
18919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
19019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
19153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic inline __u64 uptr_to_u64(void __user *ptr)
19219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
19319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return (__u64)(unsigned long)ptr;
19419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
19519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
19619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_open(struct inode *inode, struct file *file)
19719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
19819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device;
19919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client;
20019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
20196b19062e741b715cf399312c30e0672d8889569Stefan Richter	device = fw_device_get_by_devt(inode->i_rdev);
202a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg	if (device == NULL)
203a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg		return -ENODEV;
20419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
205551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(device)) {
206551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		fw_device_put(device);
207551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
208551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	}
209551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
2102d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	client = kzalloc(sizeof(*client), GFP_KERNEL);
21196b19062e741b715cf399312c30e0672d8889569Stefan Richter	if (client == NULL) {
21296b19062e741b715cf399312c30e0672d8889569Stefan Richter		fw_device_put(device);
21319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
21496b19062e741b715cf399312c30e0672d8889569Stefan Richter	}
21519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
21696b19062e741b715cf399312c30e0672d8889569Stefan Richter	client->device = device;
21719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_lock_init(&client->lock);
21845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_init(&client->resource_idr);
21945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	INIT_LIST_HEAD(&client->event_list);
22019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	init_waitqueue_head(&client->wait);
221fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kref_init(&client->kref);
22219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
22319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	file->private_data = client;
22419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
225d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_lock(&device->client_list_mutex);
22697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	list_add_tail(&client->link, &device->client_list);
227d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_unlock(&device->client_list_mutex);
22897bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
22919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
23019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
23119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
23219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void queue_event(struct client *client, struct event *event,
23319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			void *data0, size_t size0, void *data1, size_t size1)
23419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
23519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	unsigned long flags;
23619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
23719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[0].data = data0;
23819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[0].size = size0;
23919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[1].data = data1;
24019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[1].size = size1;
24119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
24219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
24345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (client->in_shutdown)
24445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		kfree(event);
24545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	else
24645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		list_add_tail(&event->link, &client->event_list);
24719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
24883431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason
24983431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason	wake_up_interruptible(&client->wait);
25019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
25119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
25253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dequeue_event(struct client *client,
25353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			 char __user *buffer, size_t count)
25419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
25519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event *event;
25619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	size_t size, total;
2572dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	int i, ret;
25819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2592dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = wait_event_interruptible(client->wait,
2602dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter			!list_empty(&client->event_list) ||
2612dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter			fw_device_is_shutdown(client->device));
2622dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
2632dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter		return ret;
26419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2652603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (list_empty(&client->event_list) &&
2662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		       fw_device_is_shutdown(client->device))
2672603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		return -ENODEV;
26819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2693ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&client->lock);
270a459b8ab9c176143fecef8ace4b70d6dbd7a8113Stefan Richter	event = list_first_entry(&client->event_list, struct event, link);
27119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	list_del(&event->link);
2723ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&client->lock);
27319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
27419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	total = 0;
27519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
27619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		size = min(event->v[i].size, count - total);
2772603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		if (copy_to_user(buffer + total, event->v[i].data, size)) {
2782dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter			ret = -EFAULT;
27919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			goto out;
2802603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		}
28119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		total += size;
28219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
2832dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = total;
28419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
28519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg out:
28619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(event);
28719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2882dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	return ret;
28919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
29019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
29153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic ssize_t fw_device_op_read(struct file *file, char __user *buffer,
29253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				 size_t count, loff_t *offset)
29319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
29419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
29519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
29619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dequeue_event(client, buffer, count);
29719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
29819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
29953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
30053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				 struct client *client)
301344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg{
302da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	struct fw_card *card = client->device->card;
303cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason
3043ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&card->lock);
305344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
306da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	event->closure	     = client->bus_reset_closure;
307344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->type          = FW_CDEV_EVENT_BUS_RESET;
308cf5a56ac8083dd04ffe8b9b2ec7895e9bcff44bcStefan Richter	event->generation    = client->device->generation;
309da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	event->node_id       = client->device->node_id;
310344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->local_node_id = card->local_node->node_id;
311344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->bm_node_id    = 0; /* FIXME: We don't track the BM. */
312344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->irm_node_id   = card->irm_node->node_id;
313344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->root_node_id  = card->root_node->node_id;
314cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason
3153ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&card->lock);
316344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg}
317344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
31853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void for_each_client(struct fw_device *device,
31953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			    void (*callback)(struct client *client))
3202603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
3212603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	struct client *c;
3222603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
323d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_lock(&device->client_list_mutex);
3242603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	list_for_each_entry(c, &device->client_list, link)
3252603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		callback(c);
326d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_unlock(&device->client_list_mutex);
3272603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
3282603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
329b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic int schedule_reallocations(int id, void *p, void *data)
330b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
3319fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	schedule_if_iso_resource(p);
332b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
333b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	return 0;
334b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
335b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
33653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void queue_bus_reset_event(struct client *client)
33797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{
33897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct bus_reset_event *e;
33997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
34097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e = kzalloc(sizeof(*e), GFP_KERNEL);
34197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (e == NULL) {
34297bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg		fw_notify("Out of memory when allocating bus reset event\n");
34397bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg		return;
34497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	}
34597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
34697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fill_bus_reset_event(&e->reset, client);
34797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
34897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	queue_event(client, &e->event,
34997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		    &e->reset, sizeof(e->reset), NULL, 0);
350b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
351b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
352b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	idr_for_each(&client->resource_idr, schedule_reallocations, client);
353b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
35497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg}
35597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
35697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergvoid fw_device_cdev_update(struct fw_device *device)
35797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{
3582603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	for_each_client(device, queue_bus_reset_event);
3592603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
36097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
3612603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void wake_up_client(struct client *client)
3622603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
3632603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	wake_up_interruptible(&client->wait);
3642603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
36597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
3662603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergvoid fw_device_cdev_remove(struct fw_device *device)
3672603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
3682603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	for_each_client(device, wake_up_client);
36997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg}
37097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
3716e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterunion ioctl_arg {
3726e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_info			get_info;
3736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_request		send_request;
3746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_allocate			allocate;
3756e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_deallocate		deallocate;
3766e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_response		send_response;
3776e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_initiate_bus_reset	initiate_bus_reset;
3786e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_add_descriptor		add_descriptor;
3796e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_remove_descriptor	remove_descriptor;
3806e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_create_iso_context	create_iso_context;
3816e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_queue_iso		queue_iso;
3826e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_start_iso		start_iso;
3836e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_stop_iso			stop_iso;
3846e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_cycle_timer		get_cycle_timer;
3856e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_allocate_iso_resource	allocate_iso_resource;
3866e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_stream_packet	send_stream_packet;
3876e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_cycle_timer2		get_cycle_timer2;
3886e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter};
3896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter
3906e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_info(struct client *client, union ioctl_arg *arg)
39119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
3926e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_info *a = &arg->get_info;
393344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	struct fw_cdev_event_bus_reset bus_reset;
394c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	unsigned long ret = 0;
395344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
3966e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	client->version = a->version;
3976e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->version = FW_CDEV_VERSION;
3986e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->card = client->device->card->index;
399344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
400c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	down_read(&fw_device_rwsem);
401c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
4026e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->rom != 0) {
4036e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		size_t want = a->rom_length;
404d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter		size_t have = client->device->config_rom_length * 4;
405344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
4066e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		ret = copy_to_user(u64_to_uptr(a->rom),
4076e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter				   client->device->config_rom, min(want, have));
408344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	}
4096e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->rom_length = client->device->config_rom_length * 4;
410344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
411c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	up_read(&fw_device_rwsem);
412c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
413c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	if (ret != 0)
414c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter		return -EFAULT;
415c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
4166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	client->bus_reset_closure = a->bus_reset_closure;
4176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->bus_reset != 0) {
418da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg		fill_bus_reset_event(&bus_reset, client);
4196e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		if (copy_to_user(u64_to_uptr(a->bus_reset),
4206e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter				 &bus_reset, sizeof(bus_reset)))
421344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg			return -EFAULT;
422344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	}
42319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
42419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
42519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
42619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
42753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int add_client_resource(struct client *client,
42853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			       struct client_resource *resource, gfp_t gfp_mask)
4293964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
4303964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	unsigned long flags;
43145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
43245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
43345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason retry:
43445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (idr_pre_get(&client->resource_idr, gfp_mask) == 0)
43545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		return -ENOMEM;
4363964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
4373964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
43845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (client->in_shutdown)
43945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		ret = -ECANCELED;
44045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	else
44145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		ret = idr_get_new(&client->resource_idr, resource,
44245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				  &resource->handle);
443b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (ret >= 0) {
444fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter		client_get(client);
4459fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_if_iso_resource(resource);
446b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
4473964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
44845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
44945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret == -EAGAIN)
45045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto retry;
45145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
45245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return ret < 0 ? ret : 0;
4533964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
4543964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
45553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int release_client_resource(struct client *client, u32 handle,
45653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				   client_resource_release_fn_t release,
457e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter				   struct client_resource **return_resource)
4583964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
459e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct client_resource *resource;
4603964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
4613ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&client->lock);
46245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (client->in_shutdown)
463e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		resource = NULL;
46445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	else
465e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		resource = idr_find(&client->resource_idr, handle);
466e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	if (resource && resource->release == release)
46745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		idr_remove(&client->resource_idr, handle);
4683ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&client->lock);
4693964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
470e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	if (!(resource && resource->release == release))
4713964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		return -EINVAL;
4723964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
473e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	if (return_resource)
474e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		*return_resource = resource;
4753964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	else
476e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		resource->release(client, resource);
4773964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
478fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
479fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
4803964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	return 0;
4813964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
4823964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
48353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_transaction(struct client *client,
48453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				struct client_resource *resource)
4853964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
48697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_resource *r = container_of(resource,
48797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			struct outbound_transaction_resource, resource);
4883964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
48997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fw_cancel_transaction(client->device->card, &r->transaction);
4903964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
4913964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
49253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void complete_transaction(struct fw_card *card, int rcode,
49353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				 void *payload, size_t length, void *data)
49419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
49597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_event *e = data;
49697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_response *rsp = &e->response;
49797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client *client = e->client;
49828cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	unsigned long flags;
49919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
50097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (length < rsp->length)
50197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		rsp->length = length;
50219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (rcode == RCODE_COMPLETE)
50397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		memcpy(rsp->data, payload, rsp->length);
50419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
50528cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
50645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	/*
507fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 * 1. If called while in shutdown, the idr tree must be left untouched.
508fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    The idr handle will be removed and the client reference will be
509fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    dropped later.
510fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 * 2. If the call chain was release_client_resource ->
511fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    release_transaction -> complete_transaction (instead of a normal
512fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    conclusion of the transaction), i.e. if this resource was already
513fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    unregistered from the idr, the client reference will be dropped
514fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	 *    by release_client_resource and we must not drop it here.
51545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	 */
516fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	if (!client->in_shutdown &&
51797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	    idr_find(&client->resource_idr, e->r.resource.handle)) {
51897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		idr_remove(&client->resource_idr, e->r.resource.handle);
519fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter		/* Drop the idr's reference */
520fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter		client_put(client);
521fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	}
52228cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
52328cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
52497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	rsp->type = FW_CDEV_EVENT_RESPONSE;
52597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	rsp->rcode = rcode;
5268401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore
5278401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	/*
52897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	 * In the case that sizeof(*rsp) doesn't align with the position of the
5298401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * data, and the read is short, preserve an extra copy of the data
5308401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * to stay compatible with a pre-2.6.27 bug.  Since the bug is harmless
5318401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * for short reads and some apps depended on it, this is both safe
5328401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * and prudent for compatibility.
5338401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 */
53497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data))
53597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		queue_event(client, &e->event, rsp, sizeof(*rsp),
53697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			    rsp->data, rsp->length);
5378401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	else
53897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length,
5398401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore			    NULL, 0);
540fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
541fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	/* Drop the transaction callback's reference */
542fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
54319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
54419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
545acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int init_request(struct client *client,
546acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter			struct fw_cdev_send_request *request,
547acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter			int destination_id, int speed)
54819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
54997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_event *e;
5501f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter	int ret;
55119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
55218e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	if (request->tcode != TCODE_STREAM_DATA &&
55318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	    (request->length > 4096 || request->length > 512 << speed))
5545d3fd692a7196a9045fb606f891f5987959b65a0Stefan Richter		return -EIO;
55519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
55697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
55797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (e == NULL)
55819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
55919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
56097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->client = client;
56197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->response.length = request->length;
56297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->response.closure = request->closure;
56319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5644f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->data &&
56597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	    copy_from_user(e->response.data,
5664f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			   u64_to_uptr(request->data), request->length)) {
5671f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter		ret = -EFAULT;
56845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
5691f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter	}
5701f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter
57197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->r.resource.release = release_transaction;
57297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
57345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0)
57445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
57528cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
576fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	/* Get a reference for the transaction callback */
577fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_get(client);
578fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
579acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	fw_send_request(client->device->card, &e->r.transaction,
580664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter			request->tcode, destination_id, request->generation,
581664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter			speed, request->offset, e->response.data,
582664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter			request->length, complete_transaction, e);
583664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter	return 0;
58419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
58545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed:
58697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(e);
5871f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter
5881f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter	return ret;
58919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
59019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
5916e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_request(struct client *client, union ioctl_arg *arg)
592acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{
5936e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	switch (arg->send_request.tcode) {
594acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_QUADLET_REQUEST:
595acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_BLOCK_REQUEST:
596acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_READ_QUADLET_REQUEST:
597acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_READ_BLOCK_REQUEST:
598acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_MASK_SWAP:
599acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_COMPARE_SWAP:
600acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_FETCH_ADD:
601acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_LITTLE_ADD:
602acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_BOUNDED_ADD:
603acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_WRAP_ADD:
604acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_VENDOR_DEPENDENT:
605acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		break;
606acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	default:
607acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		return -EINVAL;
608acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	}
609acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
6106e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_request(client, &arg->send_request, client->device->node_id,
611acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter			    client->device->max_speed);
612acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter}
613acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
614281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richterstatic inline bool is_fcp_request(struct fw_request *request)
615281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter{
616281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	return request == NULL;
617281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter}
618281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter
61953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_request(struct client *client,
62053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			    struct client_resource *resource)
6213964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
62297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_resource *r = container_of(resource,
62397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			struct inbound_transaction_resource, resource);
6243964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
625281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	if (is_fcp_request(r->request))
626281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		kfree(r->data);
627281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	else
628db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch		fw_send_response(client->device->card, r->request,
629db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch				 RCODE_CONFLICT_ERROR);
63097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
6313964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
6323964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
63397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstatic void handle_request(struct fw_card *card, struct fw_request *request,
63453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			   int tcode, int destination, int source,
63553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			   int generation, int speed,
63653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			   unsigned long long offset,
63753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			   void *payload, size_t length, void *callback_data)
63819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
63997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct address_handler_resource *handler = callback_data;
64097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_resource *r;
64197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_event *e;
642281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	void *fcp_frame = NULL;
64345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
64419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
64597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r = kmalloc(sizeof(*r), GFP_ATOMIC);
6462d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	e = kmalloc(sizeof(*e), GFP_ATOMIC);
64797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (r == NULL || e == NULL)
64845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
64919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
65097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->request = request;
65197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->data    = payload;
65297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->length  = length;
65319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
654281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	if (is_fcp_request(request)) {
655281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		/*
656281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		 * FIXME: Let core-transaction.c manage a
657281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		 * single reference-counted copy?
658281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		 */
659281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		fcp_frame = kmemdup(payload, length, GFP_ATOMIC);
660281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		if (fcp_frame == NULL)
661281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter			goto failed;
662281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter
663281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		r->data = fcp_frame;
664281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	}
665281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter
66697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->resource.release = release_request;
66797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC);
66845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0)
66945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
67019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
67119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.type    = FW_CDEV_EVENT_REQUEST;
67219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.tcode   = tcode;
67319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.offset  = offset;
67419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.length  = length;
67597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->request.handle  = r->resource.handle;
67619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	e->request.closure = handler->closure;
67719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
67897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	queue_event(handler->client, &e->event,
679281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		    &e->request, sizeof(e->request), r->data, length);
68045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return;
68145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
68245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed:
68397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
68445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	kfree(e);
685281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	kfree(fcp_frame);
686281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter
687281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	if (!is_fcp_request(request))
688db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch		fw_send_response(card, request, RCODE_CONFLICT_ERROR);
68919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
69019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
69153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_address_handler(struct client *client,
69253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				    struct client_resource *resource)
6933964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
69497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct address_handler_resource *r =
69597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	    container_of(resource, struct address_handler_resource, resource);
6963964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
69797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fw_core_remove_address_handler(&r->handler);
69897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
6993964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
7003964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
7016e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate(struct client *client, union ioctl_arg *arg)
70219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
7036e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_allocate *a = &arg->allocate;
70497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct address_handler_resource *r;
70519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_address_region region;
70645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
70719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
70897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r = kmalloc(sizeof(*r), GFP_KERNEL);
70997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (r == NULL)
71019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
71119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7126e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	region.start = a->offset;
7136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	region.end   = a->offset + a->length;
7146e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->handler.length           = a->length;
71597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->handler.address_callback = handle_request;
7166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->handler.callback_data    = r;
7176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->closure   = a->closure;
7186e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->client    = client;
71919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
72097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = fw_core_add_address_handler(&r->handler, &region);
7213e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter	if (ret < 0) {
72297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		kfree(r);
7233e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter		return ret;
72419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
72519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
72697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->resource.release = release_address_handler;
72797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(client, &r->resource, GFP_KERNEL);
72845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0) {
72997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		release_address_handler(client, &r->resource);
73045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		return ret;
73145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	}
7326e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->handle = r->resource.handle;
73319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
73419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
73519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
73619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate(struct client *client, union ioctl_arg *arg)
7389472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg{
7396e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return release_client_resource(client, arg->deallocate.handle,
74045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				       release_address_handler, NULL);
7419472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg}
7429472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg
7436e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_response(struct client *client, union ioctl_arg *arg)
74419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
7456e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_response *a = &arg->send_response;
7463964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource *resource;
74797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_resource *r;
7487e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter	int ret = 0;
74919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7506e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (release_client_resource(client, a->handle,
75145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				    release_request, &resource) < 0)
75219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
75345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
75497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r = container_of(resource, struct inbound_transaction_resource,
75597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			 resource);
756281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	if (is_fcp_request(r->request))
757281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		goto out;
758281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter
7596e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->length < r->length)
7606e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		r->length = a->length;
7616e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (copy_from_user(r->data, u64_to_uptr(a->data), r->length)) {
762281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		ret = -EFAULT;
763281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		kfree(r->request);
764281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		goto out;
7657e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter	}
7666e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	fw_send_response(client->device->card, r->request, a->rcode);
7677e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter out:
76819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(r);
76919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7707e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter	return ret;
77119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
77219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg)
7745371842b723dd04df57171f2c74660966901380cKristian Høgsberg{
7756e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return fw_core_initiate_bus_reset(client->device->card,
7766e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter			arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET);
7775371842b723dd04df57171f2c74660966901380cKristian Høgsberg}
7785371842b723dd04df57171f2c74660966901380cKristian Høgsberg
7793964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void release_descriptor(struct client *client,
7803964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			       struct client_resource *resource)
7813964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
78297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct descriptor_resource *r =
78397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		container_of(resource, struct descriptor_resource, resource);
7843964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
78597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fw_core_remove_descriptor(&r->descriptor);
78697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
7873964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
7883964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
7896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_add_descriptor(struct client *client, union ioctl_arg *arg)
79066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{
7916e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_add_descriptor *a = &arg->add_descriptor;
79297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct descriptor_resource *r;
79345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
79466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
795de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter	/* Access policy: Allow this ioctl only on local nodes' device files. */
79692368890d551794ee8d7e90477d8498bb7f82a9bStefan Richter	if (!client->device->is_local)
797de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter		return -ENOSYS;
798de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter
7996e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->length > 256)
80066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -EINVAL;
80166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
8026e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r = kmalloc(sizeof(*r) + a->length * 4, GFP_KERNEL);
80397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (r == NULL)
80466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -ENOMEM;
80566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
8066e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (copy_from_user(r->data, u64_to_uptr(a->data), a->length * 4)) {
80745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		ret = -EFAULT;
80845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
80966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	}
81066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
8116e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->descriptor.length    = a->length;
8126e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->descriptor.immediate = a->immediate;
8136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->descriptor.key       = a->key;
81497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->descriptor.data      = r->data;
81566dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
81697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = fw_core_add_descriptor(&r->descriptor);
81745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0)
81845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
81966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
82097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->resource.release = release_descriptor;
82197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(client, &r->resource, GFP_KERNEL);
82245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0) {
82397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		fw_core_remove_descriptor(&r->descriptor);
82445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
82545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	}
8266e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->handle = r->resource.handle;
82766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
82866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	return 0;
82945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed:
83097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
83145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
83245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return ret;
83366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg}
83466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
8356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_remove_descriptor(struct client *client, union ioctl_arg *arg)
83666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{
8376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return release_client_resource(client, arg->remove_descriptor.handle,
83845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				       release_descriptor, NULL);
83966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg}
84066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
84153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void iso_callback(struct fw_iso_context *context, u32 cycle,
84253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			 size_t header_length, void *header, void *data)
84319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
84419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = data;
84597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct iso_interrupt_event *e;
84619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
84797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e = kzalloc(sizeof(*e) + header_length, GFP_ATOMIC);
84897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (e == NULL)
84919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return;
85019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
85197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
85297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.closure   = client->iso_closure;
85397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.cycle     = cycle;
85497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.header_length = header_length;
85597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	memcpy(e->interrupt.header, header, header_length);
85697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	queue_event(client, &e->event, &e->interrupt,
85797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		    sizeof(e->interrupt) + header_length, NULL, 0);
85819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
85919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8606e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
86119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
8626e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_create_iso_context *a = &arg->create_iso_context;
86324315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	struct fw_iso_context *context;
86419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
865fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter	/* We only support one context at this time. */
866fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter	if (client->iso_context != NULL)
867fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter		return -EBUSY;
868fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter
8696e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->channel > 63)
87021efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg		return -EINVAL;
87121efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg
8726e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	switch (a->type) {
873c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	case FW_ISO_CONTEXT_RECEIVE:
8746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		if (a->header_size < 4 || (a->header_size & 3))
875c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg			return -EINVAL;
876c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg		break;
877c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
878c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	case FW_ISO_CONTEXT_TRANSMIT:
8796e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		if (a->speed > SCODE_3200)
880c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg			return -EINVAL;
881c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg		break;
882c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
883c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	default:
88421efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg		return -EINVAL;
885c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	}
886c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
8876e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	context = fw_iso_context_create(client->device->card, a->type,
8886e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter					a->channel, a->speed, a->header_size,
8896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter					iso_callback, client);
89024315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	if (IS_ERR(context))
89124315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg		return PTR_ERR(context);
89224315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg
8936e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	client->iso_closure = a->closure;
89424315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	client->iso_context = context;
89519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
896abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	/* We only support one context at this time. */
8976e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->handle = 0;
898abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
89919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
90019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
90119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9021ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg/* Macros for decoding the iso packet control header. */
9031ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_PAYLOAD_LENGTH(v)	((v) & 0xffff)
9041ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_INTERRUPT(v)	(((v) >> 16) & 0x01)
9051ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_SKIP(v)		(((v) >> 17) & 0x01)
9067a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_TAG(v)		(((v) >> 18) & 0x03)
9077a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_SY(v)		(((v) >> 20) & 0x0f)
9081ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_HEADER_LENGTH(v)	(((v) >> 24) & 0xff)
9091ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg
9106e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
91119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
9126e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_queue_iso *a = &arg->queue_iso;
91319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_iso_packet __user *p, *end, *next;
9149b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg	struct fw_iso_context *ctx = client->iso_context;
915ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	unsigned long payload, buffer_end, header_length;
9161ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg	u32 control;
91719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	int count;
91819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct {
91919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		struct fw_iso_packet packet;
92019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		u8 header[256];
92119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	} u;
92219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9236e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (ctx == NULL || a->handle != 0)
92419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
92519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
926c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	/*
927c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * If the user passes a non-NULL data pointer, has mmap()'ed
92819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * the iso buffer, and the pointer points inside the buffer,
92919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * we setup the payload pointers accordingly.  Otherwise we
9309aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	 * set them both to 0, which will still let packets with
93119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * payload_length == 0 through.  In other words, if no packets
93219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * use the indirect payload, the iso buffer need not be mapped
9336e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	 * and the a->data pointer is ignored.
934c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 */
93519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9366e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	payload = (unsigned long)a->data - client->vm_start;
937ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	buffer_end = client->buffer.page_count << PAGE_SHIFT;
9386e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->data == 0 || client->buffer.pages == NULL ||
939ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	    payload >= buffer_end) {
9409aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		payload = 0;
941ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg		buffer_end = 0;
94219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
94319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9446e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets);
9451ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro
9466e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (!access_ok(VERIFY_READ, p, a->size))
94719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EFAULT;
94819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9496e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	end = (void __user *)p + a->size;
95019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	count = 0;
95119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	while (p < end) {
9521ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		if (get_user(control, &p->control))
95319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EFAULT;
9541ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.payload_length = GET_PAYLOAD_LENGTH(control);
9551ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.interrupt = GET_INTERRUPT(control);
9561ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.skip = GET_SKIP(control);
9571ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.tag = GET_TAG(control);
9581ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.sy = GET_SY(control);
9591ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.header_length = GET_HEADER_LENGTH(control);
960295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg
9619b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg		if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) {
962385ab5bcd4be586dffdba550b310308d89eade71Clemens Ladisch			if (u.packet.header_length % 4 != 0)
963385ab5bcd4be586dffdba550b310308d89eade71Clemens Ladisch				return -EINVAL;
964295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			header_length = u.packet.header_length;
965295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		} else {
966c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			/*
967c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 * We require that header_length is a multiple of
968c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 * the fixed header size, ctx->header_size.
969c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg			 */
9709b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			if (ctx->header_size == 0) {
9719b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg				if (u.packet.header_length > 0)
9729b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg					return -EINVAL;
9734ba1d9c0c22947a9207029e7184733252e6135f1Clemens Ladisch			} else if (u.packet.header_length == 0 ||
9744ba1d9c0c22947a9207029e7184733252e6135f1Clemens Ladisch				   u.packet.header_length % ctx->header_size != 0) {
975295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg				return -EINVAL;
9769b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg			}
977295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			header_length = 0;
978295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		}
979295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg
98019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		next = (struct fw_cdev_iso_packet __user *)
981295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg			&p->header[header_length / 4];
98219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		if (next > end)
98319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
98419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		if (__copy_from_user
985295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		    (u.packet.header, p->header, header_length))
98619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EFAULT;
98798b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg		if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
98819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		    u.packet.header_length + u.packet.payload_length > 0)
98919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
990ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg		if (payload + u.packet.payload_length > buffer_end)
99119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
99219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
9939b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg		if (fw_iso_context_queue(ctx, &u.packet,
9949b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg					 &client->buffer, payload))
99519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			break;
99619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
99719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		p = next;
99819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		payload += u.packet.payload_length;
99919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		count++;
100019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
100119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10026e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->size    -= uptr_to_u64(p) - a->packets;
10036e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->packets  = uptr_to_u64(p);
10046e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->data     = client->vm_start + payload;
100519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
100619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return count;
100719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
100819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10096e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_start_iso(struct client *client, union ioctl_arg *arg)
101019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
10116e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_start_iso *a = &arg->start_iso;
101219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (client->iso_context == NULL || a->handle != 0)
1014abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg		return -EINVAL;
1015fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter
10166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE &&
10176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	    (a->tags == 0 || a->tags > 15 || a->sync > 15))
10186e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		return -EINVAL;
1019eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg
10206e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return fw_iso_context_start(client->iso_context,
10216e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter				    a->cycle, a->sync, a->tags);
102219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
102319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10246e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_stop_iso(struct client *client, union ioctl_arg *arg)
1025b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg{
10266e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_stop_iso *a = &arg->stop_iso;
1027abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
10286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (client->iso_context == NULL || a->handle != 0)
1029abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg		return -EINVAL;
1030abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
1031b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg	return fw_iso_context_stop(client->iso_context);
1032b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg}
1033b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg
10346e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg)
1035a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter{
10366e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2;
1037a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	struct fw_card *card = client->device->card;
1038abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	struct timespec ts = {0, 0};
10394a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter	u32 cycle_time;
1040abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	int ret = 0;
1041a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
10424a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter	local_irq_disable();
1043a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
1044168cf9af699e87d5a6f44b684583714ecabb8e71Stefan Richter	cycle_time = card->driver->get_cycle_time(card);
1045abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter
10466e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	switch (a->clk_id) {
1047abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	case CLOCK_REALTIME:      getnstimeofday(&ts);                   break;
1048abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	case CLOCK_MONOTONIC:     do_posix_clock_monotonic_gettime(&ts); break;
1049abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	case CLOCK_MONOTONIC_RAW: getrawmonotonic(&ts);                  break;
1050abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	default:
1051abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter		ret = -EINVAL;
1052abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	}
1053a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
10544a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter	local_irq_enable();
1055a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
10566e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->tv_sec      = ts.tv_sec;
10576e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->tv_nsec     = ts.tv_nsec;
10586e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->cycle_timer = cycle_time;
1059abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter
1060abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	return ret;
1061abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter}
1062abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter
10636e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg)
1064abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter{
10656e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_cycle_timer *a = &arg->get_cycle_timer;
1066abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	struct fw_cdev_get_cycle_timer2 ct2;
1067abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter
1068abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	ct2.clk_id = CLOCK_REALTIME;
10696e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	ioctl_get_cycle_timer2(client, (union ioctl_arg *)&ct2);
1070abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter
10716e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->local_time = ct2.tv_sec * USEC_PER_SEC + ct2.tv_nsec / NSEC_PER_USEC;
10726e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->cycle_timer = ct2.cycle_timer;
10734a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter
1074a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	return 0;
1075a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter}
1076a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
1077b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void iso_resource_work(struct work_struct *work)
1078b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
1079b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource_event *e;
1080b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource *r =
1081b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter			container_of(work, struct iso_resource, work.work);
1082b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct client *client = r->client;
1083b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	int generation, channel, bandwidth, todo;
1084b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	bool skip, free, success;
1085b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1086b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
1087b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	generation = client->device->generation;
1088b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	todo = r->todo;
1089b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/* Allow 1000ms grace period for other reallocations. */
1090b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (todo == ISO_RES_ALLOC &&
1091b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) {
10929fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3));
1093b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		skip = true;
1094b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	} else {
1095b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		/* We could be called twice within the same generation. */
1096b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		skip = todo == ISO_RES_REALLOC &&
1097b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		       r->generation == generation;
1098b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
10991ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	free = todo == ISO_RES_DEALLOC ||
11001ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	       todo == ISO_RES_ALLOC_ONCE ||
11011ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	       todo == ISO_RES_DEALLOC_ONCE;
1102b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->generation = generation;
1103b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
1104b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1105b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (skip)
1106b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto out;
1107b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1108b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	bandwidth = r->bandwidth;
1109b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1110b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	fw_iso_resource_manage(client->device->card, generation,
1111b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter			r->channels, &channel, &bandwidth,
11121ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter			todo == ISO_RES_ALLOC ||
11131ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter			todo == ISO_RES_REALLOC ||
11146fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter			todo == ISO_RES_ALLOC_ONCE,
11156fdc03709433ccc2005f0f593ae9d9dd04f7b485Stefan Richter			r->transaction_data);
1116b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/*
1117b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * Is this generation outdated already?  As long as this resource sticks
1118b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * in the idr, it will be scheduled again for a newer generation or at
1119b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * shutdown.
1120b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 */
1121b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (channel == -EAGAIN &&
1122b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC))
1123b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto out;
1124b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1125b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	success = channel >= 0 || bandwidth > 0;
1126b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1127b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
1128b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/*
1129b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * Transit from allocation to reallocation, except if the client
1130b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * requested deallocation in the meantime.
1131b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 */
1132b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (r->todo == ISO_RES_ALLOC)
1133b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		r->todo = ISO_RES_REALLOC;
1134b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/*
1135b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * Allocation or reallocation failure?  Pull this resource out of the
1136b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * idr and prepare for deletion, unless the client is shutting down.
1137b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 */
1138b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (r->todo == ISO_RES_REALLOC && !success &&
1139b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    !client->in_shutdown &&
1140b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    idr_find(&client->resource_idr, r->resource.handle)) {
1141b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		idr_remove(&client->resource_idr, r->resource.handle);
1142b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		client_put(client);
1143b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		free = true;
1144b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1145b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
1146b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1147b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (todo == ISO_RES_ALLOC && channel >= 0)
11485d9cb7d276a9c465fef5a771792eac2cf1929f2bStefan Richter		r->channels = 1ULL << channel;
1149b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1150b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (todo == ISO_RES_REALLOC && success)
1151b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto out;
1152b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
11531ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
1154b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		e = r->e_alloc;
1155b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		r->e_alloc = NULL;
1156b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	} else {
1157b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		e = r->e_dealloc;
1158b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		r->e_dealloc = NULL;
1159b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1160e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e->iso_resource.handle    = r->resource.handle;
1161e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e->iso_resource.channel   = channel;
1162e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e->iso_resource.bandwidth = bandwidth;
1163b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1164b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	queue_event(client, &e->event,
1165e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		    &e->iso_resource, sizeof(e->iso_resource), NULL, 0);
1166b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1167b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (free) {
1168b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		cancel_delayed_work(&r->work);
1169b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		kfree(r->e_alloc);
1170b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		kfree(r->e_dealloc);
1171b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		kfree(r);
1172b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1173b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter out:
1174b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	client_put(client);
1175b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1176b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1177b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *client,
1178b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter				 struct client_resource *resource)
1179b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
1180b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource *r =
1181b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		container_of(resource, struct iso_resource, resource);
1182b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1183b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
1184b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->todo = ISO_RES_DEALLOC;
11859fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	schedule_iso_resource(r, 0);
1186b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
1187b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1188b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
11891ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int init_iso_resource(struct client *client,
11901ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		struct fw_cdev_allocate_iso_resource *request, int todo)
1191b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
1192b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource_event *e1, *e2;
1193b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource *r;
1194b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	int ret;
1195b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1196b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if ((request->channels == 0 && request->bandwidth == 0) ||
1197b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL ||
1198b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    request->bandwidth < 0)
1199b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		return -EINVAL;
1200b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1201b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r  = kmalloc(sizeof(*r), GFP_KERNEL);
1202b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	e1 = kmalloc(sizeof(*e1), GFP_KERNEL);
1203b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	e2 = kmalloc(sizeof(*e2), GFP_KERNEL);
1204b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (r == NULL || e1 == NULL || e2 == NULL) {
1205b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		ret = -ENOMEM;
1206b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto fail;
1207b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1208b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1209b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	INIT_DELAYED_WORK(&r->work, iso_resource_work);
1210b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->client	= client;
12111ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	r->todo		= todo;
1212b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->generation	= -1;
1213b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->channels	= request->channels;
1214b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->bandwidth	= request->bandwidth;
1215b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->e_alloc	= e1;
1216b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->e_dealloc	= e2;
1217b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1218e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e1->iso_resource.closure = request->closure;
1219e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e1->iso_resource.type    = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED;
1220e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e2->iso_resource.closure = request->closure;
1221e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e2->iso_resource.type    = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
1222b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
12231ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	if (todo == ISO_RES_ALLOC) {
12241ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		r->resource.release = release_iso_resource;
12251ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		ret = add_client_resource(client, &r->resource, GFP_KERNEL);
122681610b8fbfc027a67707ff567d490819a3d55844Stefan Richter		if (ret < 0)
122781610b8fbfc027a67707ff567d490819a3d55844Stefan Richter			goto fail;
12281ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	} else {
12291ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		r->resource.release = NULL;
12301ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		r->resource.handle = -1;
12319fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_iso_resource(r, 0);
12321ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	}
1233b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	request->handle = r->resource.handle;
1234b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1235b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	return 0;
1236b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fail:
1237b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	kfree(r);
1238b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	kfree(e1);
1239b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	kfree(e2);
1240b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1241b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	return ret;
1242b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1243b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
12446e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate_iso_resource(struct client *client,
12456e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter				       union ioctl_arg *arg)
12461ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{
12476e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_iso_resource(client,
12486e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter			&arg->allocate_iso_resource, ISO_RES_ALLOC);
12491ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter}
12501ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
12516e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate_iso_resource(struct client *client,
12526e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter					 union ioctl_arg *arg)
1253b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
12546e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return release_client_resource(client,
12556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter			arg->deallocate.handle, release_iso_resource, NULL);
1256b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1257b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
12586e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate_iso_resource_once(struct client *client,
12596e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter					    union ioctl_arg *arg)
12601ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{
12616e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_iso_resource(client,
12626e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter			&arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE);
12631ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter}
12641ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
12656e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate_iso_resource_once(struct client *client,
12666e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter					      union ioctl_arg *arg)
12671ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{
12686e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_iso_resource(client,
12696e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter			&arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE);
12701ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter}
12711ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
1272c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter/*
1273c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * Returns a speed code:  Maximum speed to or from this device,
1274c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * limited by the device's link speed, the local node's link speed,
1275c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * and all PHY port speeds between the two links.
1276c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter */
12776e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_speed(struct client *client, union ioctl_arg *arg)
127833580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter{
1279c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter	return client->device->max_speed;
128033580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter}
128133580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter
12826e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_broadcast_request(struct client *client,
12836e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter					union ioctl_arg *arg)
1284acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{
12856e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_request *a = &arg->send_request;
1286acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
12876e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	switch (a->tcode) {
1288acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_QUADLET_REQUEST:
1289acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_BLOCK_REQUEST:
1290acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		break;
1291acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	default:
1292acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		return -EINVAL;
1293acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	}
1294acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
12951566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter	/* Security policy: Only allow accesses to Units Space. */
12966e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END)
12971566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter		return -EACCES;
12981566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter
12996e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_request(client, a, LOCAL_BUS | 0x3f, SCODE_100);
1300acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter}
1301acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
13026e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg)
1303f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason{
13046e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_stream_packet *a = &arg->send_stream_packet;
130518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	struct fw_cdev_send_request request;
130618e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	int dest;
1307f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason
13086e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->speed > client->device->card->link_speed ||
13096e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	    a->length > 1024 << a->speed)
131018e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter		return -EIO;
1311f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason
13126e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->tag > 3 || a->channel > 63 || a->sy > 15)
131318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter		return -EINVAL;
131418e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter
13156e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	dest = fw_stream_packet_destination_id(a->tag, a->channel, a->sy);
131618e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	request.tcode		= TCODE_STREAM_DATA;
13176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	request.length		= a->length;
13186e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	request.closure		= a->closure;
13196e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	request.data		= a->data;
13206e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	request.generation	= a->generation;
132118e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter
13226e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_request(client, &request, dest, a->speed);
1323f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason}
1324f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason
13256e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
13264f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_get_info,
13274f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_send_request,
13284f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_allocate,
13294f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_deallocate,
13304f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_send_response,
13314f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_initiate_bus_reset,
13324f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_add_descriptor,
13334f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_remove_descriptor,
13344f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_create_iso_context,
13354f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_queue_iso,
13364f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_start_iso,
13374f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	ioctl_stop_iso,
1338a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	ioctl_get_cycle_timer,
1339b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	ioctl_allocate_iso_resource,
1340b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	ioctl_deallocate_iso_resource,
13411ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	ioctl_allocate_iso_resource_once,
13421ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	ioctl_deallocate_iso_resource_once,
134333580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter	ioctl_get_speed,
1344acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	ioctl_send_broadcast_request,
1345f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason	ioctl_send_stream_packet,
1346abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	ioctl_get_cycle_timer2,
13474f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg};
13484f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
134953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dispatch_ioctl(struct client *client,
135053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			  unsigned int cmd, void __user *arg)
135119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
13526e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	union ioctl_arg buffer;
13532dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	int ret;
13544f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
135564582298b9c29535188380f488873e7d2196a2ebStefan Richter	if (fw_device_is_shutdown(client->device))
135664582298b9c29535188380f488873e7d2196a2ebStefan Richter		return -ENODEV;
135764582298b9c29535188380f488873e7d2196a2ebStefan Richter
13584f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_TYPE(cmd) != '#' ||
13594f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers))
136019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
13614f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
13624f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_DIR(cmd) & _IOC_WRITE) {
13632d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (_IOC_SIZE(cmd) > sizeof(buffer) ||
13646e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		    copy_from_user(&buffer, arg, _IOC_SIZE(cmd)))
13654f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			return -EFAULT;
13664f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	}
13674f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
13686e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer);
13692dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
13702dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter		return ret;
13714f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
13724f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_DIR(cmd) & _IOC_READ) {
13732d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg		if (_IOC_SIZE(cmd) > sizeof(buffer) ||
13746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		    copy_to_user(arg, &buffer, _IOC_SIZE(cmd)))
13754f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			return -EFAULT;
137619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
13774f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
13782dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	return ret;
137919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
138019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
138153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_ioctl(struct file *file,
138253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			       unsigned int cmd, unsigned long arg)
138319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
138464582298b9c29535188380f488873e7d2196a2ebStefan Richter	return dispatch_ioctl(file->private_data, cmd, (void __user *)arg);
138519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
138619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
138719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT
138853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_compat_ioctl(struct file *file,
138953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				      unsigned int cmd, unsigned long arg)
139019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
139164582298b9c29535188380f488873e7d2196a2ebStefan Richter	return dispatch_ioctl(file->private_data, cmd, compat_ptr(arg));
139219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
139319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif
139419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
139519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
139619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
139719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
13989aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	enum dma_data_direction direction;
13999aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	unsigned long size;
14002dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	int page_count, ret;
14019aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
1402551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(client->device))
1403551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
1404551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
14059aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	/* FIXME: We could support multiple buffers, but we don't. */
14069aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (client->buffer.pages != NULL)
14079aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EBUSY;
14089aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
14099aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (!(vma->vm_flags & VM_SHARED))
14109aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EINVAL;
141119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
14129aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (vma->vm_start & ~PAGE_MASK)
141319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
141419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
141519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	client->vm_start = vma->vm_start;
14169aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	size = vma->vm_end - vma->vm_start;
14179aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	page_count = size >> PAGE_SHIFT;
14189aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (size & ~PAGE_MASK)
14199aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EINVAL;
14209aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
14219aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (vma->vm_flags & VM_WRITE)
14229aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		direction = DMA_TO_DEVICE;
14239aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	else
14249aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		direction = DMA_FROM_DEVICE;
14259aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
14262dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = fw_iso_buffer_init(&client->buffer, client->device->card,
14272dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter				 page_count, direction);
14282dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
14292dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter		return ret;
143019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
14312dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = fw_iso_buffer_map(&client->buffer, vma);
14322dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
14339aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		fw_iso_buffer_destroy(&client->buffer, client->device->card);
14349aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
14352dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	return ret;
143619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
143719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
143845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlasonstatic int shutdown_resource(int id, void *p, void *data)
143945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason{
1440e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct client_resource *resource = p;
144145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	struct client *client = data;
144245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
1443e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	resource->release(client, resource);
1444fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
144545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
144645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return 0;
144745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason}
144845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
144919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_release(struct inode *inode, struct file *file)
145019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
145119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
1452e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct event *event, *next_event;
145319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
145497811e347310766030a648fdf0e407b2c91a39c1Stefan Richter	mutex_lock(&client->device->client_list_mutex);
145597811e347310766030a648fdf0e407b2c91a39c1Stefan Richter	list_del(&client->link);
145697811e347310766030a648fdf0e407b2c91a39c1Stefan Richter	mutex_unlock(&client->device->client_list_mutex);
145797811e347310766030a648fdf0e407b2c91a39c1Stefan Richter
145819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (client->iso_context)
145919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		fw_iso_context_destroy(client->iso_context);
146019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
146136a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter	if (client->buffer.pages)
146236a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter		fw_iso_buffer_destroy(&client->buffer, client->device->card);
146336a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter
146445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	/* Freeze client->resource_idr and client->event_list */
14653ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&client->lock);
146645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	client->in_shutdown = true;
14673ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&client->lock);
146866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
146945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_for_each(&client->resource_idr, shutdown_resource, client);
147045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_remove_all(&client->resource_idr);
147145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_destroy(&client->resource_idr);
147228cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
1473e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	list_for_each_entry_safe(event, next_event, &client->event_list, link)
1474e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		kfree(event);
147519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1476fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
147719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
147819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
147919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
148019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
148119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic unsigned int fw_device_op_poll(struct file *file, poll_table * pt)
148219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
148319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
14842603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	unsigned int mask = 0;
148519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
148619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	poll_wait(file, &client->wait, pt);
148719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
14882603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (fw_device_is_shutdown(client->device))
14892603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		mask |= POLLHUP | POLLERR;
149019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (!list_empty(&client->event_list))
14912603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		mask |= POLLIN | POLLRDNORM;
14922603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
14932603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	return mask;
149419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
149519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
149621ebcd1224d05c8673053e1e93ab9ec7ef3e0b84Stefan Richterconst struct file_operations fw_device_ops = {
149719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.owner		= THIS_MODULE,
149819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.open		= fw_device_op_open,
149919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.read		= fw_device_op_read,
150019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.unlocked_ioctl	= fw_device_op_ioctl,
150119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.poll		= fw_device_op_poll,
150219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.release	= fw_device_op_release,
150319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.mmap		= fw_device_op_mmap,
150419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
150519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT
15065af4e5eab30d481f76b89a2167c873dfad960acbStefan Richter	.compat_ioctl	= fw_device_op_compat_ioctl,
150719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif
150819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
1509