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
21eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter#include <linux/bug.h>
22be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/compat.h>
23be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/delay.h>
24be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/device.h>
250b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter#include <linux/dma-mapping.h>
26be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/errno.h>
2777c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include <linux/firewire.h>
28be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/firewire-cdev.h>
29be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/idr.h>
304a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter#include <linux/irqflags.h>
31b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/jiffies.h>
3219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/kernel.h>
33fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter#include <linux/kref.h>
34be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/mm.h>
35be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/module.h>
36d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter#include <linux/mutex.h>
3719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#include <linux/poll.h>
38ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter#include <linux/sched.h> /* required for linux/wait.h */
395a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
40cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason#include <linux/spinlock.h>
41281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter#include <linux/string.h>
42be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/time.h>
43e034d242593f12533c11742ce38c245a33e57dc7Stefan Richter#include <linux/uaccess.h>
44be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/vmalloc.h>
45be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter#include <linux/wait.h>
46b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter#include <linux/workqueue.h>
47be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter
48be5bbd6756b44602a3f281af05c2f416fa9bd1c6Stefan Richter
4977c9a5daa9c4d9b37812c9c69c7bcbb3f9399c3cStefan Richter#include "core.h"
5019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
51604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter/*
52604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter * ABI version history is documented in linux/firewire-cdev.h.
53604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter */
5418d627113b830cda80792e96b28341bcd41cf40cClemens Ladisch#define FW_CDEV_KERNEL_VERSION			5
558e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter#define FW_CDEV_VERSION_EVENT_REQUEST2		4
568e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter#define FW_CDEV_VERSION_ALLOCATE_REGION_END	4
570699a73af3811b66b1ab5650575acee5eea841abClemens Ladisch#define FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW	5
58604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter
5919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstruct client {
60344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	u32 version;
6119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device;
6245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
6319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spinlock_t lock;
6445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	bool in_shutdown;
6545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	struct idr resource_idr;
6619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct list_head event_list;
6719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	wait_queue_head_t wait;
685a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	wait_queue_head_t tx_flush_wait;
69da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	u64 bus_reset_closure;
709aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
7119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_iso_context *iso_context;
72abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg	u64 iso_closure;
739aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	struct fw_iso_buffer buffer;
749aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	unsigned long vm_start;
750b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	bool buffer_is_mapped;
7697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
77bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	struct list_head phy_receiver_link;
78bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	u64 phy_receiver_closure;
79bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
8097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg	struct list_head link;
81fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	struct kref kref;
8219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
8319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
84fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic inline void client_get(struct client *client)
85fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{
86fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kref_get(&client->kref);
87fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter}
88fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
89fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_release(struct kref *kref)
90fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{
91fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	struct client *client = container_of(kref, struct client, kref);
92fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
93fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	fw_device_put(client->device);
94fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kfree(client);
95fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter}
96fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
97fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richterstatic void client_put(struct client *client)
98fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter{
99fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kref_put(&client->kref, client_release);
100fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter}
101fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
10297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource;
10397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richtertypedef void (*client_resource_release_fn_t)(struct client *,
10497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter					     struct client_resource *);
10597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct client_resource {
10697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	client_resource_release_fn_t release;
10797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	int handle;
10897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
10997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
11097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct address_handler_resource {
11197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
11297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_address_handler handler;
11397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	__u64 closure;
11497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client *client;
11597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
11697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
11797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_resource {
11897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
11997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_transaction transaction;
12097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
12197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
12297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_resource {
12397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
12408bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason	struct fw_card *card;
12597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_request *request;
12697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	void *data;
12797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	size_t length;
12897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
12997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
13097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct descriptor_resource {
13197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client_resource resource;
13297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_descriptor descriptor;
13397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	u32 data[0];
13497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
13597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
136b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource {
137b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct client_resource resource;
138b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct client *client;
139b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/* Schedule work and access todo only with client->lock held. */
140b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct delayed_work work;
1411ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
1421ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	      ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
143b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	int generation;
144b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	u64 channels;
145b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	s32 bandwidth;
146b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource_event *e_alloc, *e_dealloc;
147b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter};
148b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
149b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *, struct client_resource *);
150b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1519fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_iso_resource(struct iso_resource *r, unsigned long delay)
1529fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{
1539fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	client_get(r->client);
154105e53f863c04e1d9e5bb34bf753c9fdbce6a60cStefan Richter	if (!queue_delayed_work(fw_workqueue, &r->work, delay))
1559fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		client_put(r->client);
1569fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter}
1579fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter
1589fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richterstatic void schedule_if_iso_resource(struct client_resource *resource)
1599fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter{
1609fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	if (resource->release == release_iso_resource)
1619fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_iso_resource(container_of(resource,
1629fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter					struct iso_resource, resource), 0);
1639fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter}
1649fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter
16597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter/*
16697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * dequeue_event() just kfree()'s the event, so the event has to be
16797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter * the first field in a struct XYZ_event.
16897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter */
16997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct event {
17097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct { void *data; size_t size; } v[2];
17197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct list_head link;
17297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
17397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
17497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct bus_reset_event {
17597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
17697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_bus_reset reset;
17797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
17897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
17997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct outbound_transaction_event {
18097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
18197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client *client;
18297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_resource r;
18397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_response response;
18497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
18597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
18697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct inbound_transaction_event {
18797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
188e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter	union {
189e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		struct fw_cdev_event_request request;
190e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		struct fw_cdev_event_request2 request2;
191e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter	} req;
19297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
19397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
19497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstruct iso_interrupt_event {
19597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct event event;
19697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_iso_interrupt interrupt;
19797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter};
19897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter
199872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richterstruct iso_interrupt_mc_event {
200872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	struct event event;
201872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	struct fw_cdev_event_iso_interrupt_mc interrupt;
202872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter};
203872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
204b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstruct iso_resource_event {
205b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct event event;
206e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct fw_cdev_event_iso_resource iso_resource;
207b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter};
208b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
209850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richterstruct outbound_phy_packet_event {
210850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	struct event event;
211850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	struct client *client;
212850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	struct fw_packet p;
213850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	struct fw_cdev_event_phy_packet phy_packet;
214850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter};
215850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter
216bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richterstruct inbound_phy_packet_event {
217bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	struct event event;
218bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	struct fw_cdev_event_phy_packet phy_packet;
219bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter};
220bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
2219c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter#ifdef CONFIG_COMPAT
2229c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richterstatic void __user *u64_to_uptr(u64 value)
2239c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter{
2249c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter	if (is_compat_task())
2259c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter		return compat_ptr(value);
2269c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter	else
2279c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter		return (void __user *)(unsigned long)value;
2289c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter}
2299c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter
2309c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richterstatic u64 uptr_to_u64(void __user *ptr)
2319c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter{
2329c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter	if (is_compat_task())
2339c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter		return ptr_to_compat(ptr);
2349c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter	else
2359c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter		return (u64)(unsigned long)ptr;
2369c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter}
2379c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter#else
2389c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richterstatic inline void __user *u64_to_uptr(u64 value)
23919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
24019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return (void __user *)(unsigned long)value;
24119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
24219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2439c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richterstatic inline u64 uptr_to_u64(void __user *ptr)
24419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
2459c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter	return (u64)(unsigned long)ptr;
24619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
2479c1176b6a28850703ea6e3a0f0c703f6d6c61cd3Stefan Richter#endif /* CONFIG_COMPAT */
24819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
24919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_open(struct inode *inode, struct file *file)
25019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
25119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_device *device;
25219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client;
25319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
25496b19062e741b715cf399312c30e0672d8889569Stefan Richter	device = fw_device_get_by_devt(inode->i_rdev);
255a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg	if (device == NULL)
256a3aca3dabbcf00f2088d472f27755c29acaa992eKristian Høgsberg		return -ENODEV;
25719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
258551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(device)) {
259551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		fw_device_put(device);
260551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
261551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	}
262551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
2632d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	client = kzalloc(sizeof(*client), GFP_KERNEL);
26496b19062e741b715cf399312c30e0672d8889569Stefan Richter	if (client == NULL) {
26596b19062e741b715cf399312c30e0672d8889569Stefan Richter		fw_device_put(device);
26619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
26796b19062e741b715cf399312c30e0672d8889569Stefan Richter	}
26819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
26996b19062e741b715cf399312c30e0672d8889569Stefan Richter	client->device = device;
27019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_lock_init(&client->lock);
27145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_init(&client->resource_idr);
27245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	INIT_LIST_HEAD(&client->event_list);
27319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	init_waitqueue_head(&client->wait);
2745a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	init_waitqueue_head(&client->tx_flush_wait);
275bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	INIT_LIST_HEAD(&client->phy_receiver_link);
27693b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter	INIT_LIST_HEAD(&client->link);
277fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	kref_init(&client->kref);
27819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
27919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	file->private_data = client;
28019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
2813ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter	return nonseekable_open(inode, file);
28219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
28319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
28419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic void queue_event(struct client *client, struct event *event,
28519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			void *data0, size_t size0, void *data1, size_t size1)
28619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
28719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	unsigned long flags;
28819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
28919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[0].data = data0;
29019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[0].size = size0;
29119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[1].data = data1;
29219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	event->v[1].size = size1;
29319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
29419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
29545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (client->in_shutdown)
29645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		kfree(event);
29745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	else
29845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		list_add_tail(&event->link, &client->event_list);
29919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
30083431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason
30183431cba3d847fc2296d3f38ce7feb623a1cfc45Jay Fenlason	wake_up_interruptible(&client->wait);
30219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
30319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
30453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dequeue_event(struct client *client,
30553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			 char __user *buffer, size_t count)
30619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
30719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct event *event;
30819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	size_t size, total;
3092dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	int i, ret;
31019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
3112dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = wait_event_interruptible(client->wait,
3122dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter			!list_empty(&client->event_list) ||
3132dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter			fw_device_is_shutdown(client->device));
3142dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
3152dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter		return ret;
31619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
3172603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (list_empty(&client->event_list) &&
3182603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		       fw_device_is_shutdown(client->device))
3192603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		return -ENODEV;
32019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
3213ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&client->lock);
322a459b8ab9c176143fecef8ace4b70d6dbd7a8113Stefan Richter	event = list_first_entry(&client->event_list, struct event, link);
32319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	list_del(&event->link);
3243ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&client->lock);
32519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
32619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	total = 0;
32719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
32819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		size = min(event->v[i].size, count - total);
3292603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		if (copy_to_user(buffer + total, event->v[i].data, size)) {
3302dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter			ret = -EFAULT;
33119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			goto out;
3322603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		}
33319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		total += size;
33419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
3352dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	ret = total;
33619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
33719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg out:
33819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(event);
33919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
3402dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	return ret;
34119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
34219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
34353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic ssize_t fw_device_op_read(struct file *file, char __user *buffer,
34453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				 size_t count, loff_t *offset)
34519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
34619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
34719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
34819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return dequeue_event(client, buffer, count);
34919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
35019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
35153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
35253dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				 struct client *client)
353344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg{
354da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	struct fw_card *card = client->device->card;
355cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason
3563ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&card->lock);
357344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
358da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	event->closure	     = client->bus_reset_closure;
359344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->type          = FW_CDEV_EVENT_BUS_RESET;
360cf5a56ac8083dd04ffe8b9b2ec7895e9bcff44bcStefan Richter	event->generation    = client->device->generation;
361da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg	event->node_id       = client->device->node_id;
362344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->local_node_id = card->local_node->node_id;
363250b2b6dd421c9f8844a867d2ac06e0661e0ad93Stefan Richter	event->bm_node_id    = card->bm_node_id;
364344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->irm_node_id   = card->irm_node->node_id;
365344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	event->root_node_id  = card->root_node->node_id;
366cf417e5494582453c033d8cac9e1352e74215435Jay Fenlason
3673ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&card->lock);
368344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg}
369344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
37053dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void for_each_client(struct fw_device *device,
37153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			    void (*callback)(struct client *client))
3722603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
3732603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	struct client *c;
3742603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
375d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_lock(&device->client_list_mutex);
3762603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	list_for_each_entry(c, &device->client_list, link)
3772603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		callback(c);
378d67cfb9613f373d76daa2c8d209629601424ca12Stefan Richter	mutex_unlock(&device->client_list_mutex);
3792603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
3802603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
381b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic int schedule_reallocations(int id, void *p, void *data)
382b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
3839fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	schedule_if_iso_resource(p);
384b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
385b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	return 0;
386b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
387b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
38853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void queue_bus_reset_event(struct client *client)
38997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{
39097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct bus_reset_event *e;
39197bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
39297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e = kzalloc(sizeof(*e), GFP_KERNEL);
393cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter	if (e == NULL)
39497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg		return;
39597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
39697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fill_bus_reset_event(&e->reset, client);
39797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
39897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	queue_event(client, &e->event,
39997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		    &e->reset, sizeof(e->reset), NULL, 0);
400b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
401b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
402b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	idr_for_each(&client->resource_idr, schedule_reallocations, client);
403b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
40497bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg}
40597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
40697bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsbergvoid fw_device_cdev_update(struct fw_device *device)
40797bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg{
4082603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	for_each_client(device, queue_bus_reset_event);
4092603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
41097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
4112603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergstatic void wake_up_client(struct client *client)
4122603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
4132603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	wake_up_interruptible(&client->wait);
4142603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg}
41597bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
4162603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsbergvoid fw_device_cdev_remove(struct fw_device *device)
4172603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg{
4182603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	for_each_client(device, wake_up_client);
41997bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg}
42097bd9efa5a4d8a70b3bafe0d1e3e1a814fdac5bcKristian Høgsberg
4216e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterunion ioctl_arg {
4226e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_info			get_info;
4236e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_request		send_request;
4246e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_allocate			allocate;
4256e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_deallocate		deallocate;
4266e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_response		send_response;
4276e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_initiate_bus_reset	initiate_bus_reset;
4286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_add_descriptor		add_descriptor;
4296e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_remove_descriptor	remove_descriptor;
4306e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_create_iso_context	create_iso_context;
4316e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_queue_iso		queue_iso;
4326e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_start_iso		start_iso;
4336e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_stop_iso			stop_iso;
4346e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_cycle_timer		get_cycle_timer;
4356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_allocate_iso_resource	allocate_iso_resource;
4366e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_stream_packet	send_stream_packet;
4376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_cycle_timer2		get_cycle_timer2;
438850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	struct fw_cdev_send_phy_packet		send_phy_packet;
439bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	struct fw_cdev_receive_phy_packets	receive_phy_packets;
440872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	struct fw_cdev_set_iso_channels		set_iso_channels;
441d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch	struct fw_cdev_flush_iso		flush_iso;
4426e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter};
4436e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter
4446e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_info(struct client *client, union ioctl_arg *arg)
44519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
4466e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_info *a = &arg->get_info;
447344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	struct fw_cdev_event_bus_reset bus_reset;
448c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	unsigned long ret = 0;
449344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
4506e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	client->version = a->version;
451604f45167824e18ad5766e51ecf1d4d65f15118dStefan Richter	a->version = FW_CDEV_KERNEL_VERSION;
4526e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->card = client->device->card->index;
453344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
454c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	down_read(&fw_device_rwsem);
455c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
4566e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->rom != 0) {
4576e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		size_t want = a->rom_length;
458d84702a5d7b500ead8db129ddea789c88764f357Stefan Richter		size_t have = client->device->config_rom_length * 4;
459344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
4606e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		ret = copy_to_user(u64_to_uptr(a->rom),
4616e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter				   client->device->config_rom, min(want, have));
462344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	}
4636e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->rom_length = client->device->config_rom_length * 4;
464344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg
465c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	up_read(&fw_device_rwsem);
466c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
467c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter	if (ret != 0)
468c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter		return -EFAULT;
469c9755e14a01987ada4063e8b4c50c2b6738d879eStefan Richter
47093b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter	mutex_lock(&client->device->client_list_mutex);
47193b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter
4726e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	client->bus_reset_closure = a->bus_reset_closure;
4736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->bus_reset != 0) {
474da8ecffaed434a12930f652898f9e86d1c2abc3eKristian Høgsberg		fill_bus_reset_event(&bus_reset, client);
475790198f74c9d1b46b6a89504361b1a844670d050Stefan Richter		/* unaligned size of bus_reset is 36 bytes */
476790198f74c9d1b46b6a89504361b1a844670d050Stefan Richter		ret = copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36);
477344bbc4de14e70d03f09bff04bb7d161b8a0d28cKristian Høgsberg	}
47893b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter	if (ret == 0 && list_empty(&client->link))
47993b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter		list_add_tail(&client->link, &client->device->client_list);
48019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
48193b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter	mutex_unlock(&client->device->client_list_mutex);
48293b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter
48393b37905f70083d6143f5f4dba0a45cc64379a62Stefan Richter	return ret ? -EFAULT : 0;
48419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
48519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
48653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int add_client_resource(struct client *client,
48753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			       struct client_resource *resource, gfp_t gfp_mask)
4883964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
4890a41981803fcd4107fff4e943afb72940ba653d2Stefan Richter	bool preload = !!(gfp_mask & __GFP_WAIT);
4903964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	unsigned long flags;
49145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
49245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
49337b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo	if (preload)
49437b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo		idr_preload(gfp_mask);
4953964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
49637b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo
49745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (client->in_shutdown)
49845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		ret = -ECANCELED;
49945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	else
50037b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo		ret = idr_alloc(&client->resource_idr, resource, 0, 0,
50137b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo				GFP_NOWAIT);
502b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (ret >= 0) {
50337b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo		resource->handle = ret;
504fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter		client_get(client);
5059fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_if_iso_resource(resource);
506b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
50745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
50837b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo	spin_unlock_irqrestore(&client->lock, flags);
50937b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo	if (preload)
51037b61890d757f606c25ac5a247572cb7d5efde96Tejun Heo		idr_preload_end();
51145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
51245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return ret < 0 ? ret : 0;
5133964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
5143964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
51553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int release_client_resource(struct client *client, u32 handle,
51653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				   client_resource_release_fn_t release,
517e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter				   struct client_resource **return_resource)
5183964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
519e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct client_resource *resource;
5203964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
5213ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&client->lock);
52245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (client->in_shutdown)
523e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		resource = NULL;
52445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	else
525e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		resource = idr_find(&client->resource_idr, handle);
526e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	if (resource && resource->release == release)
52745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		idr_remove(&client->resource_idr, handle);
5283ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&client->lock);
5293964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
530e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	if (!(resource && resource->release == release))
5313964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg		return -EINVAL;
5323964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
533e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	if (return_resource)
534e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		*return_resource = resource;
5353964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	else
536e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		resource->release(client, resource);
5373964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
538fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
539fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
5403964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	return 0;
5413964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
5423964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
54353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_transaction(struct client *client,
54453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				struct client_resource *resource)
5453964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
5463964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
5473964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
54853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void complete_transaction(struct fw_card *card, int rcode,
54953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				 void *payload, size_t length, void *data)
55019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
55197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_event *e = data;
55297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct fw_cdev_event_response *rsp = &e->response;
55397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct client *client = e->client;
55428cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	unsigned long flags;
55519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
55697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (length < rsp->length)
55797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		rsp->length = length;
55819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (rcode == RCODE_COMPLETE)
55997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		memcpy(rsp->data, payload, rsp->length);
56019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
56128cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	spin_lock_irqsave(&client->lock, flags);
5625a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	idr_remove(&client->resource_idr, e->r.resource.handle);
5635a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	if (client->in_shutdown)
5645a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch		wake_up(&client->tx_flush_wait);
56528cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg	spin_unlock_irqrestore(&client->lock, flags);
56628cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
56797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	rsp->type = FW_CDEV_EVENT_RESPONSE;
56897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	rsp->rcode = rcode;
5698401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore
5708401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	/*
57197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	 * In the case that sizeof(*rsp) doesn't align with the position of the
5728401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * data, and the read is short, preserve an extra copy of the data
5738401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * to stay compatible with a pre-2.6.27 bug.  Since the bug is harmless
5748401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * for short reads and some apps depended on it, this is both safe
5758401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 * and prudent for compatibility.
5768401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	 */
57797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data))
57897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		queue_event(client, &e->event, rsp, sizeof(*rsp),
57997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			    rsp->data, rsp->length);
5808401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore	else
58197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length,
5828401d92ba46a1e859464cbd9c9ee304f6e361da3David Moore			    NULL, 0);
583fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter
5845a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	/* Drop the idr's reference */
5855a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	client_put(client);
58619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
58719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
588acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richterstatic int init_request(struct client *client,
589acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter			struct fw_cdev_send_request *request,
590acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter			int destination_id, int speed)
59119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
59297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct outbound_transaction_event *e;
5931f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter	int ret;
59419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
59518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	if (request->tcode != TCODE_STREAM_DATA &&
59618e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	    (request->length > 4096 || request->length > 512 << speed))
5975d3fd692a7196a9045fb606f891f5987959b65a0Stefan Richter		return -EIO;
59819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
599a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch	if (request->tcode == TCODE_WRITE_QUADLET_REQUEST &&
600a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch	    request->length < 4)
601a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch		return -EINVAL;
602a8e93f3dccc066cd6dd1e9db1e35942914fc57d1Clemens Ladisch
60397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
60497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (e == NULL)
60519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
60619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
60797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->client = client;
60897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->response.length = request->length;
60997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->response.closure = request->closure;
61019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6114f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (request->data &&
61297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	    copy_from_user(e->response.data,
6134f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			   u64_to_uptr(request->data), request->length)) {
6141f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter		ret = -EFAULT;
61545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
6161f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter	}
6171f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter
61897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->r.resource.release = release_transaction;
61997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
62045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0)
62145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
62228cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
623acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	fw_send_request(client->device->card, &e->r.transaction,
624664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter			request->tcode, destination_id, request->generation,
625664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter			speed, request->offset, e->response.data,
626664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter			request->length, complete_transaction, e);
627664d8010b170ae8b3ce9268b4f4da934d27b0491Stefan Richter	return 0;
62819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
62945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed:
63097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(e);
6311f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter
6321f3125af8ed7410cc0ebcc0acd59bbfc1ae0057aStefan Richter	return ret;
63319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
63419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_request(struct client *client, union ioctl_arg *arg)
636acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{
6376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	switch (arg->send_request.tcode) {
638acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_QUADLET_REQUEST:
639acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_BLOCK_REQUEST:
640acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_READ_QUADLET_REQUEST:
641acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_READ_BLOCK_REQUEST:
642acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_MASK_SWAP:
643acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_COMPARE_SWAP:
644acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_FETCH_ADD:
645acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_LITTLE_ADD:
646acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_BOUNDED_ADD:
647acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_WRAP_ADD:
648acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_LOCK_VENDOR_DEPENDENT:
649acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		break;
650acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	default:
651acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		return -EINVAL;
652acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	}
653acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
6546e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_request(client, &arg->send_request, client->device->node_id,
655acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter			    client->device->max_speed);
656acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter}
657acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
658281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richterstatic inline bool is_fcp_request(struct fw_request *request)
659281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter{
660281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	return request == NULL;
661281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter}
662281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter
66353dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_request(struct client *client,
66453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			    struct client_resource *resource)
6653964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
66697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_resource *r = container_of(resource,
66797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			struct inbound_transaction_resource, resource);
6683964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
669281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	if (is_fcp_request(r->request))
670281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		kfree(r->data);
671281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	else
67208bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason		fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR);
6730244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter
6740244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter	fw_card_put(r->card);
67597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
6763964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
6773964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
67897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richterstatic void handle_request(struct fw_card *card, struct fw_request *request,
67953dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			   int tcode, int destination, int source,
68033e553fe2b4a983ef34a57ab1440d8d33397bb12Stefan Richter			   int generation, unsigned long long offset,
68153dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			   void *payload, size_t length, void *callback_data)
68219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
68397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct address_handler_resource *handler = callback_data;
68497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_resource *r;
68597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_event *e;
686e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter	size_t event_size0;
687281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	void *fcp_frame = NULL;
68845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
68919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
6900244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter	/* card may be different from handler->client->device->card */
6910244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter	fw_card_get(card);
6920244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter
69397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r = kmalloc(sizeof(*r), GFP_ATOMIC);
6942d826cc5c791bdc5f5651324c485746be9492be0Kristian Høgsberg	e = kmalloc(sizeof(*e), GFP_ATOMIC);
695cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter	if (r == NULL || e == NULL)
69645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
697cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter
69808bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason	r->card    = card;
69997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->request = request;
70097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->data    = payload;
70197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->length  = length;
70219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
703281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	if (is_fcp_request(request)) {
704281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		/*
705281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		 * FIXME: Let core-transaction.c manage a
706281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		 * single reference-counted copy?
707281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		 */
708281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		fcp_frame = kmemdup(payload, length, GFP_ATOMIC);
709281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		if (fcp_frame == NULL)
710281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter			goto failed;
711281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter
712281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		r->data = fcp_frame;
713281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	}
714281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter
71597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->resource.release = release_request;
71697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC);
71745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0)
71845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
71919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
720e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter	if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) {
721e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		struct fw_cdev_event_request *req = &e->req.request;
722e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter
723e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		if (tcode & 0x10)
724e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter			tcode = TCODE_LOCK_REQUEST;
725e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter
726e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->type	= FW_CDEV_EVENT_REQUEST;
727e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->tcode	= tcode;
728e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->offset	= offset;
729e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->length	= length;
730e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->handle	= r->resource.handle;
731e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->closure	= handler->closure;
732e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		event_size0	= sizeof(*req);
733e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter	} else {
734e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		struct fw_cdev_event_request2 *req = &e->req.request2;
735e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter
736e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->type	= FW_CDEV_EVENT_REQUEST2;
737e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->tcode	= tcode;
738e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->offset	= offset;
739e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->source_node_id = source;
740e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->destination_node_id = destination;
741e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->card	= card->index;
742e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->generation	= generation;
743e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->length	= length;
744e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->handle	= r->resource.handle;
745e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		req->closure	= handler->closure;
746e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		event_size0	= sizeof(*req);
747e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter	}
74819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
74997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	queue_event(handler->client, &e->event,
750e205597d188a9ea69ce43f740a14f07b3f5b996aStefan Richter		    &e->req, event_size0, r->data, length);
75145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return;
75245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
75345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed:
75497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
75545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	kfree(e);
756281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	kfree(fcp_frame);
757281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter
758281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	if (!is_fcp_request(request))
759db5d247ae811f49185a71e703b65acad845e4b18Clemens Ladisch		fw_send_response(card, request, RCODE_CONFLICT_ERROR);
7600244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter
7610244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter	fw_card_put(card);
76219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
76319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
76453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void release_address_handler(struct client *client,
76553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				    struct client_resource *resource)
7663964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
76797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct address_handler_resource *r =
76897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	    container_of(resource, struct address_handler_resource, resource);
7693964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
77097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fw_core_remove_address_handler(&r->handler);
77197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
7723964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
7733964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
7746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate(struct client *client, union ioctl_arg *arg)
77519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
7766e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_allocate *a = &arg->allocate;
77797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct address_handler_resource *r;
77819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_address_region region;
77945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
78019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
78197c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r = kmalloc(sizeof(*r), GFP_KERNEL);
78297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (r == NULL)
78319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -ENOMEM;
78419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
7856e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	region.start = a->offset;
7868e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter	if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END)
7878e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter		region.end = a->offset + a->length;
7888e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter	else
7898e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter		region.end = a->region_end;
7908e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter
7916e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->handler.length           = a->length;
79297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->handler.address_callback = handle_request;
7936e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->handler.callback_data    = r;
7946e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->closure   = a->closure;
7956e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->client    = client;
79619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
79797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = fw_core_add_address_handler(&r->handler, &region);
7983e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter	if (ret < 0) {
79997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		kfree(r);
8003e0b5f0d7cb5fef402517e41eebff5a0f0e65a13Stefan Richter		return ret;
80119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
8028e2b2b46ea4ca5ef790dddf78b360ed736a62d7cStefan Richter	a->offset = r->handler.offset;
80319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
80497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->resource.release = release_address_handler;
80597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(client, &r->resource, GFP_KERNEL);
80645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0) {
80797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		release_address_handler(client, &r->resource);
80845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		return ret;
80945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	}
8106e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->handle = r->resource.handle;
81119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
81219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
81319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
81419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8156e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate(struct client *client, union ioctl_arg *arg)
8169472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg{
8176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return release_client_resource(client, arg->deallocate.handle,
81845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				       release_address_handler, NULL);
8199472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg}
8209472316b6eab3500ded544f6e86700c33541ef4eKristian Høgsberg
8216e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_response(struct client *client, union ioctl_arg *arg)
82219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
8236e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_response *a = &arg->send_response;
8243964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg	struct client_resource *resource;
82597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct inbound_transaction_resource *r;
8267e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter	int ret = 0;
82719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (release_client_resource(client, a->handle,
82945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				    release_request, &resource) < 0)
83019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
83145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
83297c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r = container_of(resource, struct inbound_transaction_resource,
83397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter			 resource);
834281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter	if (is_fcp_request(r->request))
835281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		goto out;
836281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter
837a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch	if (a->length != fw_get_response_length(r->request)) {
838a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch		ret = -EINVAL;
839a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch		kfree(r->request);
840a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch		goto out;
841a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch	}
842a10c0ce76098857b899505d05de9f2e13ddf7a7aClemens Ladisch	if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) {
843281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		ret = -EFAULT;
844281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		kfree(r->request);
845281e20323ab72180137824a298ee9e21e6f9acf6Stefan Richter		goto out;
8467e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter	}
84708bd34c98d631fe85744d4c920c80f48a1d95f54Jay Fenlason	fw_send_response(r->card, r->request, a->rcode);
8487e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter out:
8490244f57302f7e8bebd2f1ab58767eac2e9f678a6Stefan Richter	fw_card_put(r->card);
85019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	kfree(r);
85119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8527e44c0b56b07a5e34de9943cfb2fee72e71a9f0eStefan Richter	return ret;
85319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
85419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
8556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg)
8565371842b723dd04df57171f2c74660966901380cKristian Høgsberg{
85702d37bed188c500ee7afb0a2dc6b65a80704c58eStefan Richter	fw_schedule_bus_reset(client->device->card, true,
8586e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter			arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET);
85902d37bed188c500ee7afb0a2dc6b65a80704c58eStefan Richter	return 0;
8605371842b723dd04df57171f2c74660966901380cKristian Høgsberg}
8615371842b723dd04df57171f2c74660966901380cKristian Høgsberg
8623964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsbergstatic void release_descriptor(struct client *client,
8633964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg			       struct client_resource *resource)
8643964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg{
86597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct descriptor_resource *r =
86697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		container_of(resource, struct descriptor_resource, resource);
8673964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
86897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	fw_core_remove_descriptor(&r->descriptor);
86997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
8703964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg}
8713964a4496eaa4cb84772e8dfc6c3a72ec4ddca7aKristian Høgsberg
8726e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_add_descriptor(struct client *client, union ioctl_arg *arg)
87366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{
8746e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_add_descriptor *a = &arg->add_descriptor;
87597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct descriptor_resource *r;
87645ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	int ret;
87766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
878de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter	/* Access policy: Allow this ioctl only on local nodes' device files. */
87992368890d551794ee8d7e90477d8498bb7f82a9bStefan Richter	if (!client->device->is_local)
880de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter		return -ENOSYS;
881de487da8ca5839d057e1f4b57ee3f387e180b800Stefan Richter
8826e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->length > 256)
88366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -EINVAL;
88466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
8856e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r = kmalloc(sizeof(*r) + a->length * 4, GFP_KERNEL);
88697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	if (r == NULL)
88766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg		return -ENOMEM;
88866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
8896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (copy_from_user(r->data, u64_to_uptr(a->data), a->length * 4)) {
89045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		ret = -EFAULT;
89145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
89266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	}
89366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
8946e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->descriptor.length    = a->length;
8956e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->descriptor.immediate = a->immediate;
8966e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	r->descriptor.key       = a->key;
89797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->descriptor.data      = r->data;
89866dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
89997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = fw_core_add_descriptor(&r->descriptor);
90045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0)
90145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
90266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
90397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	r->resource.release = release_descriptor;
90497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	ret = add_client_resource(client, &r->resource, GFP_KERNEL);
90545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	if (ret < 0) {
90697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		fw_core_remove_descriptor(&r->descriptor);
90745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason		goto failed;
90845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	}
9096e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->handle = r->resource.handle;
91066dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
91166dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg	return 0;
91245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason failed:
91397c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	kfree(r);
91445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
91545ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return ret;
91666dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg}
91766dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
9186e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_remove_descriptor(struct client *client, union ioctl_arg *arg)
91966dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg{
9206e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return release_client_resource(client, arg->remove_descriptor.handle,
92145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason				       release_descriptor, NULL);
92266dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg}
92366dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
92453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic void iso_callback(struct fw_iso_context *context, u32 cycle,
92553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			 size_t header_length, void *header, void *data)
92619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
92719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = data;
92897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	struct iso_interrupt_event *e;
92919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
93056d04cb189f955e5167c27944d61aa57ad69b598Stefan Richter	e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC);
931cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter	if (e == NULL)
93219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return;
933cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter
93497c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
93597c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.closure   = client->iso_closure;
93697c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.cycle     = cycle;
93797c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	e->interrupt.header_length = header_length;
93897c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	memcpy(e->interrupt.header, header, header_length);
93997c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter	queue_event(client, &e->event, &e->interrupt,
94097c18b7fd6df4ae0d32509f292a2eb0d4b26d623Stefan Richter		    sizeof(e->interrupt) + header_length, NULL, 0);
94119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
94219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
943872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richterstatic void iso_mc_callback(struct fw_iso_context *context,
944872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter			    dma_addr_t completed, void *data)
945872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter{
946872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	struct client *client = data;
947872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	struct iso_interrupt_mc_event *e;
948872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
949872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	e = kmalloc(sizeof(*e), GFP_ATOMIC);
950cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter	if (e == NULL)
951872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		return;
952cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter
953872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	e->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL;
954872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	e->interrupt.closure   = client->iso_closure;
955872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer,
956872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter						      completed);
957872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	queue_event(client, &e->event, &e->interrupt,
958872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		    sizeof(e->interrupt), NULL, 0);
959872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter}
960872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
9610b6c4857f7684f6d3f59e0506f62953575346978Stefan Richterstatic enum dma_data_direction iso_dma_direction(struct fw_iso_context *context)
9620b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter{
9630b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter		if (context->type == FW_ISO_CONTEXT_TRANSMIT)
9640b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter			return DMA_TO_DEVICE;
9650b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter		else
9660b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter			return DMA_FROM_DEVICE;
9670b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter}
9680b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter
9696e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
97019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
9716e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_create_iso_context *a = &arg->create_iso_context;
97224315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	struct fw_iso_context *context;
973872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	fw_iso_callback_t cb;
9740b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	int ret;
97519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
976eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter	BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
977872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		     FW_CDEV_ISO_CONTEXT_RECEIVE  != FW_ISO_CONTEXT_RECEIVE  ||
978872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		     FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL !=
979872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter					FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL);
98021efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg
9816e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	switch (a->type) {
982872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	case FW_ISO_CONTEXT_TRANSMIT:
983872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		if (a->speed > SCODE_3200 || a->channel > 63)
984c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg			return -EINVAL;
985872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
986872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		cb = iso_callback;
987c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg		break;
988c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
989872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	case FW_ISO_CONTEXT_RECEIVE:
990872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		if (a->header_size < 4 || (a->header_size & 3) ||
991872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		    a->channel > 63)
992c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg			return -EINVAL;
993872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
994872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		cb = iso_callback;
995872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		break;
996872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
997872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
998872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		cb = (fw_iso_callback_t)iso_mc_callback;
999c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg		break;
1000c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
1001c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	default:
100221efb3cfc6ed49991638000f58bb23b838c76e25Kristian Høgsberg		return -EINVAL;
1003c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg	}
1004c70dc788fd8d3870b41231b6a53a64afb98cfd13Kristian Høgsberg
10056e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	context = fw_iso_context_create(client->device->card, a->type,
1006872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter			a->channel, a->speed, a->header_size, cb, client);
100724315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	if (IS_ERR(context))
100824315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg		return PTR_ERR(context);
10090699a73af3811b66b1ab5650575acee5eea841abClemens Ladisch	if (client->version < FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW)
10100699a73af3811b66b1ab5650575acee5eea841abClemens Ladisch		context->drop_overflow_headers = true;
101124315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg
1012bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch	/* We only support one context at this time. */
1013bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch	spin_lock_irq(&client->lock);
1014bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch	if (client->iso_context != NULL) {
1015bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch		spin_unlock_irq(&client->lock);
1016bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch		fw_iso_context_destroy(context);
10170b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter
1018bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch		return -EBUSY;
1019bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch	}
10200b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	if (!client->buffer_is_mapped) {
10210b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter		ret = fw_iso_buffer_map_dma(&client->buffer,
10220b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter					    client->device->card,
10230b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter					    iso_dma_direction(context));
10240b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter		if (ret < 0) {
10250b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter			spin_unlock_irq(&client->lock);
10260b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter			fw_iso_context_destroy(context);
10270b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter
10280b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter			return ret;
10290b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter		}
10300b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter		client->buffer_is_mapped = true;
10310b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	}
10326e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	client->iso_closure = a->closure;
103324315c5e6f508edd84e996d67daef3d1bcc72f8bKristian Høgsberg	client->iso_context = context;
1034bdfe273ee54b29498851fc8058516037d284270cClemens Ladisch	spin_unlock_irq(&client->lock);
103519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10366e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->handle = 0;
1037abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
103819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
103919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
104019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1041872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richterstatic int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg)
1042872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter{
1043872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels;
1044872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	struct fw_iso_context *ctx = client->iso_context;
1045872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
1046872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	if (ctx == NULL || a->handle != 0)
1047872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		return -EINVAL;
1048872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
1049872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	return fw_iso_context_set_channels(ctx, &a->channels);
1050872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter}
1051872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
10521ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg/* Macros for decoding the iso packet control header. */
10531ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_PAYLOAD_LENGTH(v)	((v) & 0xffff)
10541ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_INTERRUPT(v)	(((v) >> 16) & 0x01)
10551ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_SKIP(v)		(((v) >> 17) & 0x01)
10567a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_TAG(v)		(((v) >> 18) & 0x03)
10577a1003449c693f0d57443c8786bbf19717921ae0Stefan Richter#define GET_SY(v)		(((v) >> 20) & 0x0f)
10581ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg#define GET_HEADER_LENGTH(v)	(((v) >> 24) & 0xff)
10591ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg
10606e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
106119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
10626e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_queue_iso *a = &arg->queue_iso;
106319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct fw_cdev_iso_packet __user *p, *end, *next;
10649b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg	struct fw_iso_context *ctx = client->iso_context;
1065872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	unsigned long payload, buffer_end, transmit_header_bytes = 0;
10661ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg	u32 control;
106719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	int count;
106819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct {
106919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		struct fw_iso_packet packet;
107019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		u8 header[256];
107119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	} u;
107219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
10736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (ctx == NULL || a->handle != 0)
107419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
107519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1076c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	/*
1077c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 * If the user passes a non-NULL data pointer, has mmap()'ed
107819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * the iso buffer, and the pointer points inside the buffer,
107919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * we setup the payload pointers accordingly.  Otherwise we
10809aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	 * set them both to 0, which will still let packets with
108119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * payload_length == 0 through.  In other words, if no packets
108219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	 * use the indirect payload, the iso buffer need not be mapped
10836e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	 * and the a->data pointer is ignored.
1084c781c06d119d04601727f2fbc30151e6760d536dKristian Høgsberg	 */
10856e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	payload = (unsigned long)a->data - client->vm_start;
1086ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	buffer_end = client->buffer.page_count << PAGE_SHIFT;
10876e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->data == 0 || client->buffer.pages == NULL ||
1088ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg	    payload >= buffer_end) {
10899aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		payload = 0;
1090ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg		buffer_end = 0;
109119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
109219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1093872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3)
1094872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		return -EINVAL;
10951ccc9147f6a063c42fef67acff34de18435a4a6bAl Viro
1096872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets);
10976e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (!access_ok(VERIFY_READ, p, a->size))
109819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EFAULT;
109919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
11006e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	end = (void __user *)p + a->size;
110119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	count = 0;
110219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	while (p < end) {
11031ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		if (get_user(control, &p->control))
110419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EFAULT;
11051ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.payload_length = GET_PAYLOAD_LENGTH(control);
11061ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.interrupt = GET_INTERRUPT(control);
11071ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.skip = GET_SKIP(control);
11081ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.tag = GET_TAG(control);
11091ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.sy = GET_SY(control);
11101ca31ae7cfed3e2a8e48fbf6ed6cac06495b6158Kristian Høgsberg		u.packet.header_length = GET_HEADER_LENGTH(control);
1111295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg
1112872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		switch (ctx->type) {
1113872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		case FW_ISO_CONTEXT_TRANSMIT:
1114872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter			if (u.packet.header_length & 3)
1115385ab5bcd4be586dffdba550b310308d89eade71Clemens Ladisch				return -EINVAL;
1116ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter			transmit_header_bytes = u.packet.header_length;
1117872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter			break;
1118872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
1119872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		case FW_ISO_CONTEXT_RECEIVE:
112069e61d0c07fa28a05f699723a88d49e0014019b6Stefan Richter			if (u.packet.header_length == 0 ||
112169e61d0c07fa28a05f699723a88d49e0014019b6Stefan Richter			    u.packet.header_length % ctx->header_size != 0)
1122385ab5bcd4be586dffdba550b310308d89eade71Clemens Ladisch				return -EINVAL;
1123872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter			break;
1124872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter
1125872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter		case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
1126872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter			if (u.packet.payload_length == 0 ||
1127872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter			    u.packet.payload_length & 3)
1128295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg				return -EINVAL;
1129872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter			break;
1130295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg		}
1131295e3feb92e5073ec32a3c626302d4b92c4c8a95Kristian Høgsberg
113219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		next = (struct fw_cdev_iso_packet __user *)
1133ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter			&p->header[transmit_header_bytes / 4];
113419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		if (next > end)
113519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
113619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		if (__copy_from_user
1137ae2a97661482c1d0f1aa41b837da95054d0e9a1bStefan Richter		    (u.packet.header, p->header, transmit_header_bytes))
113819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EFAULT;
113998b6cbe83b6e8db54638746c9040c7962d96b322Kristian Høgsberg		if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
114019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		    u.packet.header_length + u.packet.payload_length > 0)
114119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
1142ef370ee74b7a9cb769d50bfb73b4023ee3e37719Kristian Høgsberg		if (payload + u.packet.payload_length > buffer_end)
114319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			return -EINVAL;
114419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
11459b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg		if (fw_iso_context_queue(ctx, &u.packet,
11469b32d5f3074e9b1afaa39a360a59fd77a2214783Kristian Høgsberg					 &client->buffer, payload))
114719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg			break;
114819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
114919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		p = next;
115019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		payload += u.packet.payload_length;
115119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		count++;
115219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	}
115313882a82ee1646336c3996c93b4a560a55d2a419Clemens Ladisch	fw_iso_context_queue_flush(ctx);
115419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
11556e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->size    -= uptr_to_u64(p) - a->packets;
11566e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->packets  = uptr_to_u64(p);
11576e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->data     = client->vm_start + payload;
115819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
115919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return count;
116019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
116119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
11626e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_start_iso(struct client *client, union ioctl_arg *arg)
116319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
11646e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_start_iso *a = &arg->start_iso;
116519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1166eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter	BUILD_BUG_ON(
1167eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter	    FW_CDEV_ISO_CONTEXT_MATCH_TAG0 != FW_ISO_CONTEXT_MATCH_TAG0 ||
1168eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter	    FW_CDEV_ISO_CONTEXT_MATCH_TAG1 != FW_ISO_CONTEXT_MATCH_TAG1 ||
1169eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter	    FW_CDEV_ISO_CONTEXT_MATCH_TAG2 != FW_ISO_CONTEXT_MATCH_TAG2 ||
1170eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter	    FW_CDEV_ISO_CONTEXT_MATCH_TAG3 != FW_ISO_CONTEXT_MATCH_TAG3 ||
1171eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter	    FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS != FW_ISO_CONTEXT_MATCH_ALL_TAGS);
1172eb5b35a560510efc6bb62f05c3c82e9596cdfafeStefan Richter
11736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (client->iso_context == NULL || a->handle != 0)
1174abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg		return -EINVAL;
1175fae603121428ba83b7343c88e68a7144525ab3ebStefan Richter
11766e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE &&
11776e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	    (a->tags == 0 || a->tags > 15 || a->sync > 15))
11786e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter		return -EINVAL;
1179eb0306eac0aad0b7da18d8fbfb777f155b2c010dKristian Høgsberg
11806e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return fw_iso_context_start(client->iso_context,
11816e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter				    a->cycle, a->sync, a->tags);
118219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
118319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
11846e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_stop_iso(struct client *client, union ioctl_arg *arg)
1185b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg{
11866e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_stop_iso *a = &arg->stop_iso;
1187abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
11886e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (client->iso_context == NULL || a->handle != 0)
1189abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg		return -EINVAL;
1190abaa5743e340c23922d92c9a5a6753ea3ae71e58Kristian Høgsberg
1191b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg	return fw_iso_context_stop(client->iso_context);
1192b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg}
1193b82956685aab4a9d333714300eb8a86fed6c9ab3Kristian Høgsberg
1194d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladischstatic int ioctl_flush_iso(struct client *client, union ioctl_arg *arg)
1195d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch{
1196d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch	struct fw_cdev_flush_iso *a = &arg->flush_iso;
1197d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch
1198d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch	if (client->iso_context == NULL || a->handle != 0)
1199d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch		return -EINVAL;
1200d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch
1201d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch	return fw_iso_context_flush_completions(client->iso_context);
1202d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch}
1203d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch
12046e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg)
1205a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter{
12066e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2;
1207a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	struct fw_card *card = client->device->card;
1208abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	struct timespec ts = {0, 0};
12094a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter	u32 cycle_time;
1210abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	int ret = 0;
1211a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
12124a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter	local_irq_disable();
1213a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
12140fcff4e39323f466a47684d7c8ffa77e1be86c8aStefan Richter	cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME);
1215abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter
12166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	switch (a->clk_id) {
1217889235ce2b26cdd231c7a1d6005928ec42f11637Thomas Gleixner	case CLOCK_REALTIME:      getnstimeofday(&ts);	break;
1218889235ce2b26cdd231c7a1d6005928ec42f11637Thomas Gleixner	case CLOCK_MONOTONIC:     ktime_get_ts(&ts);	break;
1219889235ce2b26cdd231c7a1d6005928ec42f11637Thomas Gleixner	case CLOCK_MONOTONIC_RAW: getrawmonotonic(&ts);	break;
1220abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	default:
1221abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter		ret = -EINVAL;
1222abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	}
1223a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
12244a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter	local_irq_enable();
1225a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
12266e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->tv_sec      = ts.tv_sec;
12276e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->tv_nsec     = ts.tv_nsec;
12286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->cycle_timer = cycle_time;
1229abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter
1230abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	return ret;
1231abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter}
1232abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter
12336e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg)
1234abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter{
12356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_get_cycle_timer *a = &arg->get_cycle_timer;
1236abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	struct fw_cdev_get_cycle_timer2 ct2;
1237abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter
1238abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter	ct2.clk_id = CLOCK_REALTIME;
12396e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	ioctl_get_cycle_timer2(client, (union ioctl_arg *)&ct2);
1240abfe5a01ef1e463cbafdae461b693db34e308c02Stefan Richter
12416e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->local_time = ct2.tv_sec * USEC_PER_SEC + ct2.tv_nsec / NSEC_PER_USEC;
12426e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	a->cycle_timer = ct2.cycle_timer;
12434a9bde9b8ab55a2bb51b57cad215a97bcf80bae2Stefan Richter
1244a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter	return 0;
1245a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter}
1246a64408b96b5f67c2778958a230b5cfa3408a4a81Stefan Richter
1247b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void iso_resource_work(struct work_struct *work)
1248b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
1249b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource_event *e;
1250b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource *r =
1251b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter			container_of(work, struct iso_resource, work.work);
1252b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct client *client = r->client;
1253b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	int generation, channel, bandwidth, todo;
1254b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	bool skip, free, success;
1255b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1256b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
1257b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	generation = client->device->generation;
1258b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	todo = r->todo;
1259b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/* Allow 1000ms grace period for other reallocations. */
1260b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (todo == ISO_RES_ALLOC &&
1261e71084af58cf15e6043338500eeaf6281d0a62afClemens Ladisch	    time_before64(get_jiffies_64(),
1262e71084af58cf15e6043338500eeaf6281d0a62afClemens Ladisch			  client->device->card->reset_jiffies + HZ)) {
12639fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3));
1264b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		skip = true;
1265b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	} else {
1266b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		/* We could be called twice within the same generation. */
1267b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		skip = todo == ISO_RES_REALLOC &&
1268b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		       r->generation == generation;
1269b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
12701ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	free = todo == ISO_RES_DEALLOC ||
12711ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	       todo == ISO_RES_ALLOC_ONCE ||
12721ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	       todo == ISO_RES_DEALLOC_ONCE;
1273b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->generation = generation;
1274b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
1275b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1276b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (skip)
1277b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto out;
1278b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1279b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	bandwidth = r->bandwidth;
1280b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1281b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	fw_iso_resource_manage(client->device->card, generation,
1282b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter			r->channels, &channel, &bandwidth,
12831ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter			todo == ISO_RES_ALLOC ||
12841ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter			todo == ISO_RES_REALLOC ||
1285f30e6d3e419bfb5540fa82ba7eca01d578556e6bStefan Richter			todo == ISO_RES_ALLOC_ONCE);
1286b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/*
1287b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * Is this generation outdated already?  As long as this resource sticks
1288b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * in the idr, it will be scheduled again for a newer generation or at
1289b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * shutdown.
1290b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 */
1291b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (channel == -EAGAIN &&
1292b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC))
1293b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto out;
1294b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1295b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	success = channel >= 0 || bandwidth > 0;
1296b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1297b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
1298b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/*
1299b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * Transit from allocation to reallocation, except if the client
1300b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * requested deallocation in the meantime.
1301b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 */
1302b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (r->todo == ISO_RES_ALLOC)
1303b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		r->todo = ISO_RES_REALLOC;
1304b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	/*
1305b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * Allocation or reallocation failure?  Pull this resource out of the
1306b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 * idr and prepare for deletion, unless the client is shutting down.
1307b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	 */
1308b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (r->todo == ISO_RES_REALLOC && !success &&
1309b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    !client->in_shutdown &&
1310b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	    idr_find(&client->resource_idr, r->resource.handle)) {
1311b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		idr_remove(&client->resource_idr, r->resource.handle);
1312b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		client_put(client);
1313b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		free = true;
1314b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1315b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
1316b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1317b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (todo == ISO_RES_ALLOC && channel >= 0)
13185d9cb7d276a9c465fef5a771792eac2cf1929f2bStefan Richter		r->channels = 1ULL << channel;
1319b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1320b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (todo == ISO_RES_REALLOC && success)
1321b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto out;
1322b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
13231ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
1324b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		e = r->e_alloc;
1325b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		r->e_alloc = NULL;
1326b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	} else {
1327b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		e = r->e_dealloc;
1328b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		r->e_dealloc = NULL;
1329b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1330e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e->iso_resource.handle    = r->resource.handle;
1331e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e->iso_resource.channel   = channel;
1332e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e->iso_resource.bandwidth = bandwidth;
1333b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1334b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	queue_event(client, &e->event,
1335e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		    &e->iso_resource, sizeof(e->iso_resource), NULL, 0);
1336b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1337b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (free) {
1338b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		cancel_delayed_work(&r->work);
1339b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		kfree(r->e_alloc);
1340b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		kfree(r->e_dealloc);
1341b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		kfree(r);
1342b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1343b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter out:
1344b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	client_put(client);
1345b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1346b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1347b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richterstatic void release_iso_resource(struct client *client,
1348b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter				 struct client_resource *resource)
1349b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
1350b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource *r =
1351b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		container_of(resource, struct iso_resource, resource);
1352b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1353b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_lock_irq(&client->lock);
1354b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->todo = ISO_RES_DEALLOC;
13559fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter	schedule_iso_resource(r, 0);
1356b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	spin_unlock_irq(&client->lock);
1357b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1358b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
13591ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richterstatic int init_iso_resource(struct client *client,
13601ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		struct fw_cdev_allocate_iso_resource *request, int todo)
1361b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
1362b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource_event *e1, *e2;
1363b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	struct iso_resource *r;
1364b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	int ret;
1365b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1366b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if ((request->channels == 0 && request->bandwidth == 0) ||
1367bdabfa54635e5d95a5aa2087794f203cc1915f7aStefan Richter	    request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL)
1368b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		return -EINVAL;
1369b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1370b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r  = kmalloc(sizeof(*r), GFP_KERNEL);
1371b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	e1 = kmalloc(sizeof(*e1), GFP_KERNEL);
1372b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	e2 = kmalloc(sizeof(*e2), GFP_KERNEL);
1373b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	if (r == NULL || e1 == NULL || e2 == NULL) {
1374b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		ret = -ENOMEM;
1375b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter		goto fail;
1376b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	}
1377b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1378b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	INIT_DELAYED_WORK(&r->work, iso_resource_work);
1379b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->client	= client;
13801ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	r->todo		= todo;
1381b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->generation	= -1;
1382b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->channels	= request->channels;
1383b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->bandwidth	= request->bandwidth;
1384b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->e_alloc	= e1;
1385b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	r->e_dealloc	= e2;
1386b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1387e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e1->iso_resource.closure = request->closure;
1388e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e1->iso_resource.type    = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED;
1389e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e2->iso_resource.closure = request->closure;
1390e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	e2->iso_resource.type    = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
1391b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
13921ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	if (todo == ISO_RES_ALLOC) {
13931ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		r->resource.release = release_iso_resource;
13941ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		ret = add_client_resource(client, &r->resource, GFP_KERNEL);
139581610b8fbfc027a67707ff567d490819a3d55844Stefan Richter		if (ret < 0)
139681610b8fbfc027a67707ff567d490819a3d55844Stefan Richter			goto fail;
13971ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	} else {
13981ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		r->resource.release = NULL;
13991ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter		r->resource.handle = -1;
14009fb551bf72929b316abb6d96cfb2ec05e896042aStefan Richter		schedule_iso_resource(r, 0);
14011ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter	}
1402b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	request->handle = r->resource.handle;
1403b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1404b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	return 0;
1405b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter fail:
1406b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	kfree(r);
1407b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	kfree(e1);
1408b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	kfree(e2);
1409b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
1410b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter	return ret;
1411b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1412b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
14136e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate_iso_resource(struct client *client,
14146e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter				       union ioctl_arg *arg)
14151ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{
14166e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_iso_resource(client,
14176e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter			&arg->allocate_iso_resource, ISO_RES_ALLOC);
14181ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter}
14191ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
14206e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate_iso_resource(struct client *client,
14216e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter					 union ioctl_arg *arg)
1422b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter{
14236e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return release_client_resource(client,
14246e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter			arg->deallocate.handle, release_iso_resource, NULL);
1425b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter}
1426b1bda4cdc2037447bd66753bf5ccab66d91b0b59Jay Fenlason, Stefan Richter
14276e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_allocate_iso_resource_once(struct client *client,
14286e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter					    union ioctl_arg *arg)
14291ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{
14306e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_iso_resource(client,
14316e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter			&arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE);
14321ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter}
14331ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
14346e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_deallocate_iso_resource_once(struct client *client,
14356e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter					      union ioctl_arg *arg)
14361ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter{
14376e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_iso_resource(client,
14386e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter			&arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE);
14391ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter}
14401ec3c0269d7196118cc7c403654ca5f19ef4d584Stefan Richter
1441c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter/*
1442c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * Returns a speed code:  Maximum speed to or from this device,
1443c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * limited by the device's link speed, the local node's link speed,
1444c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter * and all PHY port speeds between the two links.
1445c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter */
14466e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_get_speed(struct client *client, union ioctl_arg *arg)
144733580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter{
1448c8a25900f35e575938c791507894c036c0f2ca7dStefan Richter	return client->device->max_speed;
144933580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter}
145033580a3ef5ba3bc0ee1b520df82a24bb37ce28f0Stefan Richter
14516e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_broadcast_request(struct client *client,
14526e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter					union ioctl_arg *arg)
1453acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter{
14546e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_request *a = &arg->send_request;
1455acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
14566e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	switch (a->tcode) {
1457acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_QUADLET_REQUEST:
1458acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	case TCODE_WRITE_BLOCK_REQUEST:
1459acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		break;
1460acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	default:
1461acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter		return -EINVAL;
1462acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter	}
1463acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
14641566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter	/* Security policy: Only allow accesses to Units Space. */
14656e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END)
14661566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter		return -EACCES;
14671566f3dc3e5986a16c7bbb3bb95bb691251a8d25Stefan Richter
14686e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_request(client, a, LOCAL_BUS | 0x3f, SCODE_100);
1469acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter}
1470acfe8333572cad5dc70fce18ac966be0446548d7Jay Fenlason, Stefan Richter
14716e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg)
1472f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason{
14736e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	struct fw_cdev_send_stream_packet *a = &arg->send_stream_packet;
147418e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	struct fw_cdev_send_request request;
147518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	int dest;
1476f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason
14776e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->speed > client->device->card->link_speed ||
14786e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	    a->length > 1024 << a->speed)
147918e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter		return -EIO;
1480f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason
14816e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	if (a->tag > 3 || a->channel > 63 || a->sy > 15)
148218e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter		return -EINVAL;
148318e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter
14846e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	dest = fw_stream_packet_destination_id(a->tag, a->channel, a->sy);
148518e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter	request.tcode		= TCODE_STREAM_DATA;
14866e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	request.length		= a->length;
14876e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	request.closure		= a->closure;
14886e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	request.data		= a->data;
14896e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	request.generation	= a->generation;
149018e9b10fcdc090d3a38606958167d5923c7099b7Stefan Richter
14916e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	return init_request(client, &request, dest, a->speed);
1492f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason}
1493f8c2287c65f8f72000102fc058232669e4540bc4Jay Fenlason
1494850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richterstatic void outbound_phy_packet_callback(struct fw_packet *packet,
1495850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter					 struct fw_card *card, int status)
1496850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter{
1497850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	struct outbound_phy_packet_event *e =
1498850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter		container_of(packet, struct outbound_phy_packet_event, p);
1499850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter
1500850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	switch (status) {
1501850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	/* expected: */
1502850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	case ACK_COMPLETE:	e->phy_packet.rcode = RCODE_COMPLETE;	break;
1503850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	/* should never happen with PHY packets: */
1504850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	case ACK_PENDING:	e->phy_packet.rcode = RCODE_COMPLETE;	break;
1505850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	case ACK_BUSY_X:
1506850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	case ACK_BUSY_A:
1507850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	case ACK_BUSY_B:	e->phy_packet.rcode = RCODE_BUSY;	break;
1508850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	case ACK_DATA_ERROR:	e->phy_packet.rcode = RCODE_DATA_ERROR;	break;
1509850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	case ACK_TYPE_ERROR:	e->phy_packet.rcode = RCODE_TYPE_ERROR;	break;
1510850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	/* stale generation; cancelled; on certain controllers: no ack */
1511850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	default:		e->phy_packet.rcode = status;		break;
1512850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	}
1513cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter	e->phy_packet.data[0] = packet->timestamp;
1514850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter
1515cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter	queue_event(e->client, &e->event, &e->phy_packet,
1516cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter		    sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0);
1517850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	client_put(e->client);
1518850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter}
1519850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter
1520850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richterstatic int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)
1521850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter{
1522850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet;
1523850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	struct fw_card *card = client->device->card;
1524850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	struct outbound_phy_packet_event *e;
1525850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter
1526850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	/* Access policy: Allow this ioctl only on local nodes' device files. */
1527850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	if (!client->device->is_local)
1528850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter		return -ENOSYS;
1529850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter
1530cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter	e = kzalloc(sizeof(*e) + 4, GFP_KERNEL);
1531850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	if (e == NULL)
1532850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter		return -ENOMEM;
1533850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter
1534850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	client_get(client);
1535850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	e->client		= client;
1536850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	e->p.speed		= SCODE_100;
1537850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	e->p.generation		= a->generation;
15385b06db166c4d38638980283505259fa165d4f369Clemens Ladisch	e->p.header[0]		= TCODE_LINK_INTERNAL << 4;
15395b06db166c4d38638980283505259fa165d4f369Clemens Ladisch	e->p.header[1]		= a->data[0];
15405b06db166c4d38638980283505259fa165d4f369Clemens Ladisch	e->p.header[2]		= a->data[1];
15415b06db166c4d38638980283505259fa165d4f369Clemens Ladisch	e->p.header_length	= 12;
1542850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	e->p.callback		= outbound_phy_packet_callback;
1543850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	e->phy_packet.closure	= a->closure;
1544850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	e->phy_packet.type	= FW_CDEV_EVENT_PHY_PACKET_SENT;
1545cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter	if (is_ping_packet(a->data))
1546cc550216ae9a2993ef3973464714dc1a39ab1f86Stefan Richter			e->phy_packet.length = 4;
1547850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter
1548850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	card->driver->send_request(card, &e->p);
1549850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter
1550850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	return 0;
1551850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter}
1552850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter
1553bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richterstatic int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg)
1554bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter{
1555bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets;
1556bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	struct fw_card *card = client->device->card;
1557bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
1558bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	/* Access policy: Allow this ioctl only on local nodes' device files. */
1559bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	if (!client->device->is_local)
1560bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter		return -ENOSYS;
1561bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
1562bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	spin_lock_irq(&card->lock);
1563bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
1564bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list);
1565bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	client->phy_receiver_closure = a->closure;
1566bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
1567bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	spin_unlock_irq(&card->lock);
1568bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
1569bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	return 0;
1570bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter}
1571bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
1572bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richtervoid fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p)
1573bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter{
1574bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	struct client *client;
1575bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	struct inbound_phy_packet_event *e;
1576bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	unsigned long flags;
1577bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
1578bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	spin_lock_irqsave(&card->lock, flags);
1579bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
1580bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) {
1581bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter		e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC);
1582cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter		if (e == NULL)
1583bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter			break;
1584cfb0c9d1ffbf930a4a852f178b161c522b21b0abStefan Richter
1585bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter		e->phy_packet.closure	= client->phy_receiver_closure;
1586bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter		e->phy_packet.type	= FW_CDEV_EVENT_PHY_PACKET_RECEIVED;
1587bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter		e->phy_packet.rcode	= RCODE_COMPLETE;
1588bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter		e->phy_packet.length	= 8;
1589bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter		e->phy_packet.data[0]	= p->header[1];
1590bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter		e->phy_packet.data[1]	= p->header[2];
1591bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter		queue_event(client, &e->event,
1592bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter			    &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0);
1593bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	}
1594bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
1595bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	spin_unlock_irqrestore(&card->lock, flags);
1596bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter}
1597bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
15986e95dea728f4af36c033fcf2318529bd46dae540Stefan Richterstatic int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
1599b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x00] = ioctl_get_info,
1600b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x01] = ioctl_send_request,
1601b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x02] = ioctl_allocate,
1602b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x03] = ioctl_deallocate,
1603b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x04] = ioctl_send_response,
1604b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x05] = ioctl_initiate_bus_reset,
1605b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x06] = ioctl_add_descriptor,
1606b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x07] = ioctl_remove_descriptor,
1607b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x08] = ioctl_create_iso_context,
1608b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x09] = ioctl_queue_iso,
1609b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x0a] = ioctl_start_iso,
1610b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x0b] = ioctl_stop_iso,
1611b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x0c] = ioctl_get_cycle_timer,
1612b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x0d] = ioctl_allocate_iso_resource,
1613b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x0e] = ioctl_deallocate_iso_resource,
1614b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x0f] = ioctl_allocate_iso_resource_once,
1615b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x10] = ioctl_deallocate_iso_resource_once,
1616b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x11] = ioctl_get_speed,
1617b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x12] = ioctl_send_broadcast_request,
1618b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x13] = ioctl_send_stream_packet,
1619b9dc61cf404165fb77e80c853e9fec9af258f9ceStefan Richter	[0x14] = ioctl_get_cycle_timer2,
1620850bb6f23b93c04ce1e4509a87fa607dc17d97c1Stefan Richter	[0x15] = ioctl_send_phy_packet,
1621bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	[0x16] = ioctl_receive_phy_packets,
1622872e330e38806d835bd6c311c93ab998e2fb9058Stefan Richter	[0x17] = ioctl_set_iso_channels,
1623d1bbd20972936b9b178fda3eb1ec417cb27fdc01Clemens Ladisch	[0x18] = ioctl_flush_iso,
16244f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg};
16254f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
162653dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic int dispatch_ioctl(struct client *client,
162753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			  unsigned int cmd, void __user *arg)
162819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
16296e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	union ioctl_arg buffer;
16302dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	int ret;
16314f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
163264582298b9c29535188380f488873e7d2196a2ebStefan Richter	if (fw_device_is_shutdown(client->device))
163364582298b9c29535188380f488873e7d2196a2ebStefan Richter		return -ENODEV;
163464582298b9c29535188380f488873e7d2196a2ebStefan Richter
16354f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg	if (_IOC_TYPE(cmd) != '#' ||
16369cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) ||
16379cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter	    _IOC_SIZE(cmd) > sizeof(buffer))
1638d873d794235efa590ab3c94d5ee22bb1fab19ac4Stefan Richter		return -ENOTTY;
16394f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
1640eaca2d8e75e90a70a63a6695c9f61932609db212Stefan Richter	memset(&buffer, 0, sizeof(buffer));
16419cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter
16429cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter	if (_IOC_DIR(cmd) & _IOC_WRITE)
16439cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter		if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd)))
16444f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			return -EFAULT;
16454f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
16466e95dea728f4af36c033fcf2318529bd46dae540Stefan Richter	ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer);
16472dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
16482dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter		return ret;
16494f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
16509cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter	if (_IOC_DIR(cmd) & _IOC_READ)
16519cac00b8f0079d5d3d54ec4dae453d58dec30e7cStefan Richter		if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd)))
16524f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg			return -EFAULT;
16534f2592232ea951e52b2faf1abf519e13856ac6f4Kristian Høgsberg
16542dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	return ret;
165519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
165619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
165753dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_ioctl(struct file *file,
165853dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter			       unsigned int cmd, unsigned long arg)
165919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
166064582298b9c29535188380f488873e7d2196a2ebStefan Richter	return dispatch_ioctl(file->private_data, cmd, (void __user *)arg);
166119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
166219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
166319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT
166453dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richterstatic long fw_device_op_compat_ioctl(struct file *file,
166553dca51175cc2f66d21aeb1e70146cca65c53dadStefan Richter				      unsigned int cmd, unsigned long arg)
166619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
166764582298b9c29535188380f488873e7d2196a2ebStefan Richter	return dispatch_ioctl(file->private_data, cmd, compat_ptr(arg));
166819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
166919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif
167019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
167119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
167219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
167319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
16749aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	unsigned long size;
16752dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	int page_count, ret;
16769aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
1677551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason	if (fw_device_is_shutdown(client->device))
1678551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason		return -ENODEV;
1679551f4cb9de716ffcdaf968c99a450c22ff12e8c3Jay Fenlason
16809aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	/* FIXME: We could support multiple buffers, but we don't. */
16819aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (client->buffer.pages != NULL)
16829aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EBUSY;
16839aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
16849aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (!(vma->vm_flags & VM_SHARED))
16859aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EINVAL;
168619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
16879aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (vma->vm_start & ~PAGE_MASK)
168819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		return -EINVAL;
168919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
169019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	client->vm_start = vma->vm_start;
16919aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	size = vma->vm_end - vma->vm_start;
16929aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	page_count = size >> PAGE_SHIFT;
16939aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg	if (size & ~PAGE_MASK)
16949aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg		return -EINVAL;
16959aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
16960b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	ret = fw_iso_buffer_alloc(&client->buffer, page_count);
16972dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
16982dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter		return ret;
169919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
17000b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	spin_lock_irq(&client->lock);
17010b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	if (client->iso_context) {
17020b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter		ret = fw_iso_buffer_map_dma(&client->buffer,
17030b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter				client->device->card,
17040b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter				iso_dma_direction(client->iso_context));
17050b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter		client->buffer_is_mapped = (ret == 0);
17060b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	}
17070b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	spin_unlock_irq(&client->lock);
17082dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	if (ret < 0)
17090b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter		goto fail;
17109aad8125389a7a2990dee72d7892e22330a945ebKristian Høgsberg
17110b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	ret = fw_iso_buffer_map_vma(&client->buffer, vma);
17120b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	if (ret < 0)
17130b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter		goto fail;
17140b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter
17150b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	return 0;
17160b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter fail:
17170b6c4857f7684f6d3f59e0506f62953575346978Stefan Richter	fw_iso_buffer_destroy(&client->buffer, client->device->card);
17182dbd7d7e2327b0c2cc4e2de903e1cfa19980a504Stefan Richter	return ret;
171919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
172019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
17215a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladischstatic int is_outbound_transaction_resource(int id, void *p, void *data)
17225a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch{
17235a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	struct client_resource *resource = p;
17245a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch
17255a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	return resource->release == release_transaction;
17265a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch}
17275a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch
17285a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladischstatic int has_outbound_transactions(struct client *client)
17295a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch{
17305a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	int ret;
17315a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch
17325a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	spin_lock_irq(&client->lock);
17335a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	ret = idr_for_each(&client->resource_idr,
17345a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch			   is_outbound_transaction_resource, NULL);
17355a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	spin_unlock_irq(&client->lock);
17365a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch
17375a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	return ret;
17385a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch}
17395a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch
174045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlasonstatic int shutdown_resource(int id, void *p, void *data)
174145ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason{
1742e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct client_resource *resource = p;
174345ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	struct client *client = data;
174445ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
1745e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	resource->release(client, resource);
1746fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
174745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
174845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	return 0;
174945ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason}
175045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason
175119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic int fw_device_op_release(struct inode *inode, struct file *file)
175219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
175319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
1754e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	struct event *event, *next_event;
175519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1756bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	spin_lock_irq(&client->device->card->lock);
1757bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	list_del(&client->phy_receiver_link);
1758bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter	spin_unlock_irq(&client->device->card->lock);
1759bf54e1462b9192fdef7ea9e2bc44fdc16a4b87bcStefan Richter
176097811e347310766030a648fdf0e407b2c91a39c1Stefan Richter	mutex_lock(&client->device->client_list_mutex);
176197811e347310766030a648fdf0e407b2c91a39c1Stefan Richter	list_del(&client->link);
176297811e347310766030a648fdf0e407b2c91a39c1Stefan Richter	mutex_unlock(&client->device->client_list_mutex);
176397811e347310766030a648fdf0e407b2c91a39c1Stefan Richter
176419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (client->iso_context)
176519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg		fw_iso_context_destroy(client->iso_context);
176619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
176736a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter	if (client->buffer.pages)
176836a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter		fw_iso_buffer_destroy(&client->buffer, client->device->card);
176936a755cfc398fc50abc74055d4478c1b067dac55Stefan Richter
177045ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	/* Freeze client->resource_idr and client->event_list */
17713ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_lock_irq(&client->lock);
177245ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	client->in_shutdown = true;
17733ba949868a6dc082b24cba5c3bf3f50de7391433Stefan Richter	spin_unlock_irq(&client->lock);
177466dea3e5f69abfdfa46b091ea117e497758351e7Kristian Høgsberg
17755a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch	wait_event(client->tx_flush_wait, !has_outbound_transactions(client));
17765a5e62da9be255439e8ce59f96828775b7b33374Clemens Ladisch
177745ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_for_each(&client->resource_idr, shutdown_resource, client);
177845ee3199eb3e4233b755a9bb353a0527a4c58b5fJay Fenlason	idr_destroy(&client->resource_idr);
177928cf6a04c82857d562968dc3a8a89726e6ac3dcbKristian Høgsberg
1780e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter	list_for_each_entry_safe(event, next_event, &client->event_list, link)
1781e21fcf798e246202d7b60e864f1d7302ebaaf41cStefan Richter		kfree(event);
178219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
1783fb4430367b0bbee2420132faf16c7c762a39c0bbStefan Richter	client_put(client);
178419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
178519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	return 0;
178619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
178719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
178819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsbergstatic unsigned int fw_device_op_poll(struct file *file, poll_table * pt)
178919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg{
179019a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	struct client *client = file->private_data;
17912603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	unsigned int mask = 0;
179219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
179319a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	poll_wait(file, &client->wait, pt);
179419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
17952603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	if (fw_device_is_shutdown(client->device))
17962603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		mask |= POLLHUP | POLLERR;
179719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	if (!list_empty(&client->event_list))
17982603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg		mask |= POLLIN | POLLRDNORM;
17992603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg
18002603bf219e9bef3396b96b65326de7db27958c95Kristian Høgsberg	return mask;
180119a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg}
180219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg
180321ebcd1224d05c8673053e1e93ab9ec7ef3e0b84Stefan Richterconst struct file_operations fw_device_ops = {
180419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.owner		= THIS_MODULE,
18053ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter	.llseek		= no_llseek,
180619a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.open		= fw_device_op_open,
180719a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.read		= fw_device_op_read,
180819a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.unlocked_ioctl	= fw_device_op_ioctl,
180919a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg	.mmap		= fw_device_op_mmap,
18103ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter	.release	= fw_device_op_release,
18113ac26b2ee30005930117fe6a180c139c5f300fafStefan Richter	.poll		= fw_device_op_poll,
181219a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#ifdef CONFIG_COMPAT
18135af4e5eab30d481f76b89a2167c873dfad960acbStefan Richter	.compat_ioctl	= fw_device_op_compat_ioctl,
181419a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg#endif
181519a15b937b26638933307bb02f7b1801310d6eb2Kristian Høgsberg};
1816