xref: /linux-6.15/drivers/firewire/core-cdev.c (revision ca2c7365)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e71d31daSStefan Richter /*
3e71d31daSStefan Richter  * Char device for device raw access
4e71d31daSStefan Richter  *
5e71d31daSStefan Richter  * Copyright (C) 2005-2007  Kristian Hoegsberg <[email protected]>
6e71d31daSStefan Richter  */
7e71d31daSStefan Richter 
8eb5b35a5SStefan Richter #include <linux/bug.h>
9e71d31daSStefan Richter #include <linux/compat.h>
10e71d31daSStefan Richter #include <linux/delay.h>
11e71d31daSStefan Richter #include <linux/device.h>
120b6c4857SStefan Richter #include <linux/dma-mapping.h>
13ebe4560eSOscar Carter #include <linux/err.h>
14e71d31daSStefan Richter #include <linux/errno.h>
15e71d31daSStefan Richter #include <linux/firewire.h>
16e71d31daSStefan Richter #include <linux/firewire-cdev.h>
174a9bde9bSStefan Richter #include <linux/irqflags.h>
18e71d31daSStefan Richter #include <linux/jiffies.h>
19e71d31daSStefan Richter #include <linux/kernel.h>
20e71d31daSStefan Richter #include <linux/kref.h>
21e71d31daSStefan Richter #include <linux/mm.h>
22e71d31daSStefan Richter #include <linux/module.h>
23e71d31daSStefan Richter #include <linux/mutex.h>
24e71d31daSStefan Richter #include <linux/poll.h>
25ae2a9766SStefan Richter #include <linux/sched.h> /* required for linux/wait.h */
265a0e3ad6STejun Heo #include <linux/slab.h>
27e71d31daSStefan Richter #include <linux/spinlock.h>
28281e2032SStefan Richter #include <linux/string.h>
29e71d31daSStefan Richter #include <linux/time.h>
30e034d242SStefan Richter #include <linux/uaccess.h>
31e71d31daSStefan Richter #include <linux/vmalloc.h>
32e71d31daSStefan Richter #include <linux/wait.h>
33e71d31daSStefan Richter #include <linux/workqueue.h>
34e71d31daSStefan Richter 
35e71d31daSStefan Richter 
36e71d31daSStefan Richter #include "core.h"
371a4c53cfSTakashi Sakamoto #include <trace/events/firewire.h>
38e71d31daSStefan Richter 
399b6ad6a0STakashi Sakamoto #include "packet-header-definitions.h"
409b6ad6a0STakashi Sakamoto 
41604f4516SStefan Richter /*
42604f4516SStefan Richter  * ABI version history is documented in linux/firewire-cdev.h.
43604f4516SStefan Richter  */
4418d62711SClemens Ladisch #define FW_CDEV_KERNEL_VERSION			5
45e205597dSStefan Richter #define FW_CDEV_VERSION_EVENT_REQUEST2		4
468e2b2b46SStefan Richter #define FW_CDEV_VERSION_ALLOCATE_REGION_END	4
470699a73aSClemens Ladisch #define FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW	5
486add87e9STakashi Sakamoto #define FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP	6
49604f4516SStefan Richter 
50e71d31daSStefan Richter struct client {
51e71d31daSStefan Richter 	u32 version;
52e71d31daSStefan Richter 	struct fw_device *device;
53e71d31daSStefan Richter 
54e71d31daSStefan Richter 	spinlock_t lock;
55e71d31daSStefan Richter 	bool in_shutdown;
56d9f6c64eSTakashi Sakamoto 	struct xarray resource_xa;
57e71d31daSStefan Richter 	struct list_head event_list;
58e71d31daSStefan Richter 	wait_queue_head_t wait;
595a5e62daSClemens Ladisch 	wait_queue_head_t tx_flush_wait;
60e71d31daSStefan Richter 	u64 bus_reset_closure;
61e71d31daSStefan Richter 
62e71d31daSStefan Richter 	struct fw_iso_context *iso_context;
63e71d31daSStefan Richter 	u64 iso_closure;
64e71d31daSStefan Richter 	struct fw_iso_buffer buffer;
65e71d31daSStefan Richter 	unsigned long vm_start;
660b6c4857SStefan Richter 	bool buffer_is_mapped;
67e71d31daSStefan Richter 
68bf54e146SStefan Richter 	struct list_head phy_receiver_link;
69bf54e146SStefan Richter 	u64 phy_receiver_closure;
70bf54e146SStefan Richter 
71e71d31daSStefan Richter 	struct list_head link;
72e71d31daSStefan Richter 	struct kref kref;
73e71d31daSStefan Richter };
74e71d31daSStefan Richter 
client_get(struct client * client)75e71d31daSStefan Richter static inline void client_get(struct client *client)
76e71d31daSStefan Richter {
77e71d31daSStefan Richter 	kref_get(&client->kref);
78e71d31daSStefan Richter }
79e71d31daSStefan Richter 
client_release(struct kref * kref)80e71d31daSStefan Richter static void client_release(struct kref *kref)
81e71d31daSStefan Richter {
82e71d31daSStefan Richter 	struct client *client = container_of(kref, struct client, kref);
83e71d31daSStefan Richter 
84e71d31daSStefan Richter 	fw_device_put(client->device);
85e71d31daSStefan Richter 	kfree(client);
86e71d31daSStefan Richter }
87e71d31daSStefan Richter 
client_put(struct client * client)88e71d31daSStefan Richter static void client_put(struct client *client)
89e71d31daSStefan Richter {
90e71d31daSStefan Richter 	kref_put(&client->kref, client_release);
91e71d31daSStefan Richter }
92e71d31daSStefan Richter 
93e71d31daSStefan Richter struct client_resource;
94e71d31daSStefan Richter typedef void (*client_resource_release_fn_t)(struct client *,
95e71d31daSStefan Richter 					     struct client_resource *);
96e71d31daSStefan Richter struct client_resource {
97e71d31daSStefan Richter 	client_resource_release_fn_t release;
98e71d31daSStefan Richter 	int handle;
99e71d31daSStefan Richter };
100e71d31daSStefan Richter 
101e71d31daSStefan Richter struct address_handler_resource {
102e71d31daSStefan Richter 	struct client_resource resource;
103e71d31daSStefan Richter 	struct fw_address_handler handler;
104e71d31daSStefan Richter 	__u64 closure;
105e71d31daSStefan Richter 	struct client *client;
106e71d31daSStefan Richter };
107e71d31daSStefan Richter 
108e71d31daSStefan Richter struct outbound_transaction_resource {
109e71d31daSStefan Richter 	struct client_resource resource;
110e71d31daSStefan Richter 	struct fw_transaction transaction;
111e71d31daSStefan Richter };
112e71d31daSStefan Richter 
113e71d31daSStefan Richter struct inbound_transaction_resource {
114e71d31daSStefan Richter 	struct client_resource resource;
11508bd34c9SJay Fenlason 	struct fw_card *card;
116e71d31daSStefan Richter 	struct fw_request *request;
117e6996002STakashi Sakamoto 	bool is_fcp;
118e71d31daSStefan Richter 	void *data;
119e71d31daSStefan Richter 	size_t length;
120e71d31daSStefan Richter };
121e71d31daSStefan Richter 
122e71d31daSStefan Richter struct descriptor_resource {
123e71d31daSStefan Richter 	struct client_resource resource;
124e71d31daSStefan Richter 	struct fw_descriptor descriptor;
125c38e7e21SGustavo A. R. Silva 	u32 data[];
126e71d31daSStefan Richter };
127e71d31daSStefan Richter 
128e71d31daSStefan Richter struct iso_resource {
129e71d31daSStefan Richter 	struct client_resource resource;
130e71d31daSStefan Richter 	struct client *client;
131e71d31daSStefan Richter 	/* Schedule work and access todo only with client->lock held. */
132e71d31daSStefan Richter 	struct delayed_work work;
133e71d31daSStefan Richter 	enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
134e71d31daSStefan Richter 	      ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
135e71d31daSStefan Richter 	int generation;
136e71d31daSStefan Richter 	u64 channels;
137e71d31daSStefan Richter 	s32 bandwidth;
138e71d31daSStefan Richter 	struct iso_resource_event *e_alloc, *e_dealloc;
139e71d31daSStefan Richter };
140e71d31daSStefan Richter 
to_address_handler_resource(struct client_resource * resource)141ced2da31STakashi Sakamoto static struct address_handler_resource *to_address_handler_resource(struct client_resource *resource)
142ced2da31STakashi Sakamoto {
143ced2da31STakashi Sakamoto 	return container_of(resource, struct address_handler_resource, resource);
144ced2da31STakashi Sakamoto }
145ced2da31STakashi Sakamoto 
to_inbound_transaction_resource(struct client_resource * resource)146ced2da31STakashi Sakamoto static struct inbound_transaction_resource *to_inbound_transaction_resource(struct client_resource *resource)
147ced2da31STakashi Sakamoto {
148ced2da31STakashi Sakamoto 	return container_of(resource, struct inbound_transaction_resource, resource);
149ced2da31STakashi Sakamoto }
150ced2da31STakashi Sakamoto 
to_descriptor_resource(struct client_resource * resource)151ced2da31STakashi Sakamoto static struct descriptor_resource *to_descriptor_resource(struct client_resource *resource)
152ced2da31STakashi Sakamoto {
153ced2da31STakashi Sakamoto 	return container_of(resource, struct descriptor_resource, resource);
154ced2da31STakashi Sakamoto }
155ced2da31STakashi Sakamoto 
to_iso_resource(struct client_resource * resource)156ced2da31STakashi Sakamoto static struct iso_resource *to_iso_resource(struct client_resource *resource)
157ced2da31STakashi Sakamoto {
158ced2da31STakashi Sakamoto 	return container_of(resource, struct iso_resource, resource);
159ced2da31STakashi Sakamoto }
160ced2da31STakashi Sakamoto 
161e71d31daSStefan Richter static void release_iso_resource(struct client *, struct client_resource *);
162e71d31daSStefan Richter 
is_iso_resource(const struct client_resource * resource)16358ee62c2STakashi Sakamoto static int is_iso_resource(const struct client_resource *resource)
16458ee62c2STakashi Sakamoto {
16558ee62c2STakashi Sakamoto 	return resource->release == release_iso_resource;
16658ee62c2STakashi Sakamoto }
16758ee62c2STakashi Sakamoto 
1686ec9e926STakashi Sakamoto static void release_transaction(struct client *client,
1696ec9e926STakashi Sakamoto 				struct client_resource *resource);
1706ec9e926STakashi Sakamoto 
is_outbound_transaction_resource(const struct client_resource * resource)1716ec9e926STakashi Sakamoto static int is_outbound_transaction_resource(const struct client_resource *resource)
1726ec9e926STakashi Sakamoto {
1736ec9e926STakashi Sakamoto 	return resource->release == release_transaction;
1746ec9e926STakashi Sakamoto }
1756ec9e926STakashi Sakamoto 
schedule_iso_resource(struct iso_resource * r,unsigned long delay)1769fb551bfSStefan Richter static void schedule_iso_resource(struct iso_resource *r, unsigned long delay)
1779fb551bfSStefan Richter {
1789fb551bfSStefan Richter 	client_get(r->client);
179105e53f8SStefan Richter 	if (!queue_delayed_work(fw_workqueue, &r->work, delay))
1809fb551bfSStefan Richter 		client_put(r->client);
1819fb551bfSStefan Richter }
1829fb551bfSStefan Richter 
183e71d31daSStefan Richter /*
184e71d31daSStefan Richter  * dequeue_event() just kfree()'s the event, so the event has to be
185e71d31daSStefan Richter  * the first field in a struct XYZ_event.
186e71d31daSStefan Richter  */
187e71d31daSStefan Richter struct event {
188e71d31daSStefan Richter 	struct { void *data; size_t size; } v[2];
189e71d31daSStefan Richter 	struct list_head link;
190e71d31daSStefan Richter };
191e71d31daSStefan Richter 
192e71d31daSStefan Richter struct bus_reset_event {
193e71d31daSStefan Richter 	struct event event;
194e71d31daSStefan Richter 	struct fw_cdev_event_bus_reset reset;
195e71d31daSStefan Richter };
196e71d31daSStefan Richter 
197e71d31daSStefan Richter struct outbound_transaction_event {
198e71d31daSStefan Richter 	struct event event;
199e71d31daSStefan Richter 	struct client *client;
200e71d31daSStefan Richter 	struct outbound_transaction_resource r;
201147e9d3aSTakashi Sakamoto 	union {
202147e9d3aSTakashi Sakamoto 		struct fw_cdev_event_response without_tstamp;
203d8527cabSTakashi Sakamoto 		struct fw_cdev_event_response2 with_tstamp;
204147e9d3aSTakashi Sakamoto 	} rsp;
205e71d31daSStefan Richter };
206e71d31daSStefan Richter 
207e71d31daSStefan Richter struct inbound_transaction_event {
208e71d31daSStefan Richter 	struct event event;
209e205597dSStefan Richter 	union {
210e71d31daSStefan Richter 		struct fw_cdev_event_request request;
211e205597dSStefan Richter 		struct fw_cdev_event_request2 request2;
212865efffbSTakashi Sakamoto 		struct fw_cdev_event_request3 with_tstamp;
213e205597dSStefan Richter 	} req;
214e71d31daSStefan Richter };
215e71d31daSStefan Richter 
216e71d31daSStefan Richter struct iso_interrupt_event {
217e71d31daSStefan Richter 	struct event event;
218e71d31daSStefan Richter 	struct fw_cdev_event_iso_interrupt interrupt;
219e71d31daSStefan Richter };
220e71d31daSStefan Richter 
221872e330eSStefan Richter struct iso_interrupt_mc_event {
222872e330eSStefan Richter 	struct event event;
223872e330eSStefan Richter 	struct fw_cdev_event_iso_interrupt_mc interrupt;
224872e330eSStefan Richter };
225872e330eSStefan Richter 
226e71d31daSStefan Richter struct iso_resource_event {
227e71d31daSStefan Richter 	struct event event;
228e21fcf79SStefan Richter 	struct fw_cdev_event_iso_resource iso_resource;
229e71d31daSStefan Richter };
230e71d31daSStefan Richter 
231850bb6f2SStefan Richter struct outbound_phy_packet_event {
232850bb6f2SStefan Richter 	struct event event;
233850bb6f2SStefan Richter 	struct client *client;
234850bb6f2SStefan Richter 	struct fw_packet p;
2351ef14771STakashi Sakamoto 	union {
2361ef14771STakashi Sakamoto 		struct fw_cdev_event_phy_packet without_tstamp;
237fe971f91STakashi Sakamoto 		struct fw_cdev_event_phy_packet2 with_tstamp;
2381ef14771STakashi Sakamoto 	} phy_packet;
239850bb6f2SStefan Richter };
240850bb6f2SStefan Richter 
241bf54e146SStefan Richter struct inbound_phy_packet_event {
242bf54e146SStefan Richter 	struct event event;
2431ef14771STakashi Sakamoto 	union {
2441ef14771STakashi Sakamoto 		struct fw_cdev_event_phy_packet without_tstamp;
245fe971f91STakashi Sakamoto 		struct fw_cdev_event_phy_packet2 with_tstamp;
2461ef14771STakashi Sakamoto 	} phy_packet;
247bf54e146SStefan Richter };
248bf54e146SStefan Richter 
2499c1176b6SStefan Richter #ifdef CONFIG_COMPAT
u64_to_uptr(u64 value)2509c1176b6SStefan Richter static void __user *u64_to_uptr(u64 value)
2519c1176b6SStefan Richter {
252a25045ffSAndy Lutomirski 	if (in_compat_syscall())
2539c1176b6SStefan Richter 		return compat_ptr(value);
2549c1176b6SStefan Richter 	else
2559c1176b6SStefan Richter 		return (void __user *)(unsigned long)value;
2569c1176b6SStefan Richter }
2579c1176b6SStefan Richter 
uptr_to_u64(void __user * ptr)2589c1176b6SStefan Richter static u64 uptr_to_u64(void __user *ptr)
2599c1176b6SStefan Richter {
260a25045ffSAndy Lutomirski 	if (in_compat_syscall())
2619c1176b6SStefan Richter 		return ptr_to_compat(ptr);
2629c1176b6SStefan Richter 	else
2639c1176b6SStefan Richter 		return (u64)(unsigned long)ptr;
2649c1176b6SStefan Richter }
2659c1176b6SStefan Richter #else
u64_to_uptr(u64 value)2669c1176b6SStefan Richter static inline void __user *u64_to_uptr(u64 value)
267e71d31daSStefan Richter {
268e71d31daSStefan Richter 	return (void __user *)(unsigned long)value;
269e71d31daSStefan Richter }
270e71d31daSStefan Richter 
uptr_to_u64(void __user * ptr)2719c1176b6SStefan Richter static inline u64 uptr_to_u64(void __user *ptr)
272e71d31daSStefan Richter {
2739c1176b6SStefan Richter 	return (u64)(unsigned long)ptr;
274e71d31daSStefan Richter }
2759c1176b6SStefan Richter #endif /* CONFIG_COMPAT */
276e71d31daSStefan Richter 
fw_device_op_open(struct inode * inode,struct file * file)277e71d31daSStefan Richter static int fw_device_op_open(struct inode *inode, struct file *file)
278e71d31daSStefan Richter {
279e71d31daSStefan Richter 	struct fw_device *device;
280e71d31daSStefan Richter 	struct client *client;
281e71d31daSStefan Richter 
282e71d31daSStefan Richter 	device = fw_device_get_by_devt(inode->i_rdev);
283e71d31daSStefan Richter 	if (device == NULL)
284e71d31daSStefan Richter 		return -ENODEV;
285e71d31daSStefan Richter 
286e71d31daSStefan Richter 	if (fw_device_is_shutdown(device)) {
287e71d31daSStefan Richter 		fw_device_put(device);
288e71d31daSStefan Richter 		return -ENODEV;
289e71d31daSStefan Richter 	}
290e71d31daSStefan Richter 
291e71d31daSStefan Richter 	client = kzalloc(sizeof(*client), GFP_KERNEL);
292e71d31daSStefan Richter 	if (client == NULL) {
293e71d31daSStefan Richter 		fw_device_put(device);
294e71d31daSStefan Richter 		return -ENOMEM;
295e71d31daSStefan Richter 	}
296e71d31daSStefan Richter 
297e71d31daSStefan Richter 	client->device = device;
298e71d31daSStefan Richter 	spin_lock_init(&client->lock);
299d9f6c64eSTakashi Sakamoto 	xa_init_flags(&client->resource_xa, XA_FLAGS_ALLOC1 | XA_FLAGS_LOCK_BH);
300e71d31daSStefan Richter 	INIT_LIST_HEAD(&client->event_list);
301e71d31daSStefan Richter 	init_waitqueue_head(&client->wait);
3025a5e62daSClemens Ladisch 	init_waitqueue_head(&client->tx_flush_wait);
303bf54e146SStefan Richter 	INIT_LIST_HEAD(&client->phy_receiver_link);
30493b37905SStefan Richter 	INIT_LIST_HEAD(&client->link);
305e71d31daSStefan Richter 	kref_init(&client->kref);
306e71d31daSStefan Richter 
307e71d31daSStefan Richter 	file->private_data = client;
308e71d31daSStefan Richter 
3093ac26b2eSStefan Richter 	return nonseekable_open(inode, file);
310e71d31daSStefan Richter }
311e71d31daSStefan Richter 
queue_event(struct client * client,struct event * event,void * data0,size_t size0,void * data1,size_t size1)312e71d31daSStefan Richter static void queue_event(struct client *client, struct event *event,
313e71d31daSStefan Richter 			void *data0, size_t size0, void *data1, size_t size1)
314e71d31daSStefan Richter {
315e71d31daSStefan Richter 	event->v[0].data = data0;
316e71d31daSStefan Richter 	event->v[0].size = size0;
317e71d31daSStefan Richter 	event->v[1].data = data1;
318e71d31daSStefan Richter 	event->v[1].size = size1;
319e71d31daSStefan Richter 
3204f1f91aeSTakashi Sakamoto 	scoped_guard(spinlock_irqsave, &client->lock) {
321e71d31daSStefan Richter 		if (client->in_shutdown)
322e71d31daSStefan Richter 			kfree(event);
323e71d31daSStefan Richter 		else
324e71d31daSStefan Richter 			list_add_tail(&event->link, &client->event_list);
3254f1f91aeSTakashi Sakamoto 	}
326e71d31daSStefan Richter 
327e71d31daSStefan Richter 	wake_up_interruptible(&client->wait);
328e71d31daSStefan Richter }
329e71d31daSStefan Richter 
dequeue_event(struct client * client,char __user * buffer,size_t count)330e71d31daSStefan Richter static int dequeue_event(struct client *client,
331e71d31daSStefan Richter 			 char __user *buffer, size_t count)
332e71d31daSStefan Richter {
333e71d31daSStefan Richter 	struct event *event;
334e71d31daSStefan Richter 	size_t size, total;
335e71d31daSStefan Richter 	int i, ret;
336e71d31daSStefan Richter 
337e71d31daSStefan Richter 	ret = wait_event_interruptible(client->wait,
338e71d31daSStefan Richter 			!list_empty(&client->event_list) ||
339e71d31daSStefan Richter 			fw_device_is_shutdown(client->device));
340e71d31daSStefan Richter 	if (ret < 0)
341e71d31daSStefan Richter 		return ret;
342e71d31daSStefan Richter 
343e71d31daSStefan Richter 	if (list_empty(&client->event_list) &&
344e71d31daSStefan Richter 		       fw_device_is_shutdown(client->device))
345e71d31daSStefan Richter 		return -ENODEV;
346e71d31daSStefan Richter 
3474f1f91aeSTakashi Sakamoto 	scoped_guard(spinlock_irq, &client->lock) {
348e71d31daSStefan Richter 		event = list_first_entry(&client->event_list, struct event, link);
349e71d31daSStefan Richter 		list_del(&event->link);
3504f1f91aeSTakashi Sakamoto 	}
351e71d31daSStefan Richter 
352e71d31daSStefan Richter 	total = 0;
353e71d31daSStefan Richter 	for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
354e71d31daSStefan Richter 		size = min(event->v[i].size, count - total);
355e71d31daSStefan Richter 		if (copy_to_user(buffer + total, event->v[i].data, size)) {
356e71d31daSStefan Richter 			ret = -EFAULT;
357e71d31daSStefan Richter 			goto out;
358e71d31daSStefan Richter 		}
359e71d31daSStefan Richter 		total += size;
360e71d31daSStefan Richter 	}
361e71d31daSStefan Richter 	ret = total;
362e71d31daSStefan Richter 
363e71d31daSStefan Richter  out:
364e71d31daSStefan Richter 	kfree(event);
365e71d31daSStefan Richter 
366e71d31daSStefan Richter 	return ret;
367e71d31daSStefan Richter }
368e71d31daSStefan Richter 
fw_device_op_read(struct file * file,char __user * buffer,size_t count,loff_t * offset)369e71d31daSStefan Richter static ssize_t fw_device_op_read(struct file *file, char __user *buffer,
370e71d31daSStefan Richter 				 size_t count, loff_t *offset)
371e71d31daSStefan Richter {
372e71d31daSStefan Richter 	struct client *client = file->private_data;
373e71d31daSStefan Richter 
374e71d31daSStefan Richter 	return dequeue_event(client, buffer, count);
375e71d31daSStefan Richter }
376e71d31daSStefan Richter 
fill_bus_reset_event(struct fw_cdev_event_bus_reset * event,struct client * client)377e71d31daSStefan Richter static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
378e71d31daSStefan Richter 				 struct client *client)
379e71d31daSStefan Richter {
380e71d31daSStefan Richter 	struct fw_card *card = client->device->card;
381e71d31daSStefan Richter 
38227310d56STakashi Sakamoto 	guard(spinlock_irq)(&card->lock);
383e71d31daSStefan Richter 
384e71d31daSStefan Richter 	event->closure	     = client->bus_reset_closure;
385e71d31daSStefan Richter 	event->type          = FW_CDEV_EVENT_BUS_RESET;
386e71d31daSStefan Richter 	event->generation    = client->device->generation;
387e71d31daSStefan Richter 	event->node_id       = client->device->node_id;
388e71d31daSStefan Richter 	event->local_node_id = card->local_node->node_id;
389250b2b6dSStefan Richter 	event->bm_node_id    = card->bm_node_id;
390e71d31daSStefan Richter 	event->irm_node_id   = card->irm_node->node_id;
391e71d31daSStefan Richter 	event->root_node_id  = card->root_node->node_id;
392e71d31daSStefan Richter }
393e71d31daSStefan Richter 
for_each_client(struct fw_device * device,void (* callback)(struct client * client))394e71d31daSStefan Richter static void for_each_client(struct fw_device *device,
395e71d31daSStefan Richter 			    void (*callback)(struct client *client))
396e71d31daSStefan Richter {
397e71d31daSStefan Richter 	struct client *c;
398e71d31daSStefan Richter 
399044ce581STakashi Sakamoto 	guard(mutex)(&device->client_list_mutex);
400044ce581STakashi Sakamoto 
401e71d31daSStefan Richter 	list_for_each_entry(c, &device->client_list, link)
402e71d31daSStefan Richter 		callback(c);
403e71d31daSStefan Richter }
404e71d31daSStefan Richter 
queue_bus_reset_event(struct client * client)405e71d31daSStefan Richter static void queue_bus_reset_event(struct client *client)
406e71d31daSStefan Richter {
407e71d31daSStefan Richter 	struct bus_reset_event *e;
4086ec9e926STakashi Sakamoto 	struct client_resource *resource;
409d9f6c64eSTakashi Sakamoto 	unsigned long index;
410e71d31daSStefan Richter 
411e71d31daSStefan Richter 	e = kzalloc(sizeof(*e), GFP_KERNEL);
412cfb0c9d1SStefan Richter 	if (e == NULL)
413e71d31daSStefan Richter 		return;
414e71d31daSStefan Richter 
415e71d31daSStefan Richter 	fill_bus_reset_event(&e->reset, client);
416e71d31daSStefan Richter 
417e71d31daSStefan Richter 	queue_event(client, &e->event,
418e71d31daSStefan Richter 		    &e->reset, sizeof(e->reset), NULL, 0);
419e71d31daSStefan Richter 
420d3816b8bSTakashi Sakamoto 	guard(spinlock_irq)(&client->lock);
421d3816b8bSTakashi Sakamoto 
422d9f6c64eSTakashi Sakamoto 	xa_for_each(&client->resource_xa, index, resource) {
4236ec9e926STakashi Sakamoto 		if (is_iso_resource(resource))
4246ec9e926STakashi Sakamoto 			schedule_iso_resource(to_iso_resource(resource), 0);
4256ec9e926STakashi Sakamoto 	}
426e71d31daSStefan Richter }
427e71d31daSStefan Richter 
fw_device_cdev_update(struct fw_device * device)428e71d31daSStefan Richter void fw_device_cdev_update(struct fw_device *device)
429e71d31daSStefan Richter {
430e71d31daSStefan Richter 	for_each_client(device, queue_bus_reset_event);
431e71d31daSStefan Richter }
432e71d31daSStefan Richter 
wake_up_client(struct client * client)433e71d31daSStefan Richter static void wake_up_client(struct client *client)
434e71d31daSStefan Richter {
435e71d31daSStefan Richter 	wake_up_interruptible(&client->wait);
436e71d31daSStefan Richter }
437e71d31daSStefan Richter 
fw_device_cdev_remove(struct fw_device * device)438e71d31daSStefan Richter void fw_device_cdev_remove(struct fw_device *device)
439e71d31daSStefan Richter {
440e71d31daSStefan Richter 	for_each_client(device, wake_up_client);
441e71d31daSStefan Richter }
442e71d31daSStefan Richter 
4436e95dea7SStefan Richter union ioctl_arg {
4446e95dea7SStefan Richter 	struct fw_cdev_get_info			get_info;
4456e95dea7SStefan Richter 	struct fw_cdev_send_request		send_request;
4466e95dea7SStefan Richter 	struct fw_cdev_allocate			allocate;
4476e95dea7SStefan Richter 	struct fw_cdev_deallocate		deallocate;
4486e95dea7SStefan Richter 	struct fw_cdev_send_response		send_response;
4496e95dea7SStefan Richter 	struct fw_cdev_initiate_bus_reset	initiate_bus_reset;
4506e95dea7SStefan Richter 	struct fw_cdev_add_descriptor		add_descriptor;
4516e95dea7SStefan Richter 	struct fw_cdev_remove_descriptor	remove_descriptor;
4526e95dea7SStefan Richter 	struct fw_cdev_create_iso_context	create_iso_context;
4536e95dea7SStefan Richter 	struct fw_cdev_queue_iso		queue_iso;
4546e95dea7SStefan Richter 	struct fw_cdev_start_iso		start_iso;
4556e95dea7SStefan Richter 	struct fw_cdev_stop_iso			stop_iso;
4566e95dea7SStefan Richter 	struct fw_cdev_get_cycle_timer		get_cycle_timer;
4576e95dea7SStefan Richter 	struct fw_cdev_allocate_iso_resource	allocate_iso_resource;
4586e95dea7SStefan Richter 	struct fw_cdev_send_stream_packet	send_stream_packet;
4596e95dea7SStefan Richter 	struct fw_cdev_get_cycle_timer2		get_cycle_timer2;
460850bb6f2SStefan Richter 	struct fw_cdev_send_phy_packet		send_phy_packet;
461bf54e146SStefan Richter 	struct fw_cdev_receive_phy_packets	receive_phy_packets;
462872e330eSStefan Richter 	struct fw_cdev_set_iso_channels		set_iso_channels;
463d1bbd209SClemens Ladisch 	struct fw_cdev_flush_iso		flush_iso;
4646e95dea7SStefan Richter };
4656e95dea7SStefan Richter 
ioctl_get_info(struct client * client,union ioctl_arg * arg)4666e95dea7SStefan Richter static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
467e71d31daSStefan Richter {
4686e95dea7SStefan Richter 	struct fw_cdev_get_info *a = &arg->get_info;
469e71d31daSStefan Richter 	struct fw_cdev_event_bus_reset bus_reset;
470e71d31daSStefan Richter 	unsigned long ret = 0;
471e71d31daSStefan Richter 
4726e95dea7SStefan Richter 	client->version = a->version;
473604f4516SStefan Richter 	a->version = FW_CDEV_KERNEL_VERSION;
4746e95dea7SStefan Richter 	a->card = client->device->card->index;
475e71d31daSStefan Richter 
4762a6a58f0STakashi Sakamoto 	scoped_guard(rwsem_read, &fw_device_rwsem) {
4776e95dea7SStefan Richter 		if (a->rom != 0) {
4786e95dea7SStefan Richter 			size_t want = a->rom_length;
479e71d31daSStefan Richter 			size_t have = client->device->config_rom_length * 4;
480e71d31daSStefan Richter 
4812a6a58f0STakashi Sakamoto 			ret = copy_to_user(u64_to_uptr(a->rom), client->device->config_rom,
4822a6a58f0STakashi Sakamoto 					   min(want, have));
483e71d31daSStefan Richter 			if (ret != 0)
484e71d31daSStefan Richter 				return -EFAULT;
4852a6a58f0STakashi Sakamoto 		}
4862a6a58f0STakashi Sakamoto 		a->rom_length = client->device->config_rom_length * 4;
4872a6a58f0STakashi Sakamoto 	}
488e71d31daSStefan Richter 
489044ce581STakashi Sakamoto 	guard(mutex)(&client->device->client_list_mutex);
49093b37905SStefan Richter 
4916e95dea7SStefan Richter 	client->bus_reset_closure = a->bus_reset_closure;
4926e95dea7SStefan Richter 	if (a->bus_reset != 0) {
493e71d31daSStefan Richter 		fill_bus_reset_event(&bus_reset, client);
494790198f7SStefan Richter 		/* unaligned size of bus_reset is 36 bytes */
495790198f7SStefan Richter 		ret = copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36);
496e71d31daSStefan Richter 	}
49793b37905SStefan Richter 	if (ret == 0 && list_empty(&client->link))
49893b37905SStefan Richter 		list_add_tail(&client->link, &client->device->client_list);
499e71d31daSStefan Richter 
50093b37905SStefan Richter 	return ret ? -EFAULT : 0;
501e71d31daSStefan Richter }
502e71d31daSStefan Richter 
add_client_resource(struct client * client,struct client_resource * resource,gfp_t gfp_mask)503d9f6c64eSTakashi Sakamoto static int add_client_resource(struct client *client, struct client_resource *resource,
504d9f6c64eSTakashi Sakamoto 			       gfp_t gfp_mask)
505e71d31daSStefan Richter {
506e71d31daSStefan Richter 	int ret;
507e71d31daSStefan Richter 
508d3816b8bSTakashi Sakamoto 	scoped_guard(spinlock_irqsave, &client->lock) {
509d9f6c64eSTakashi Sakamoto 		u32 index;
510d9f6c64eSTakashi Sakamoto 
511d9f6c64eSTakashi Sakamoto 		if (client->in_shutdown) {
512e71d31daSStefan Richter 			ret = -ECANCELED;
513d9f6c64eSTakashi Sakamoto 		} else {
514d9f6c64eSTakashi Sakamoto 			if (gfpflags_allow_blocking(gfp_mask)) {
515d9f6c64eSTakashi Sakamoto 				ret = xa_alloc(&client->resource_xa, &index, resource, xa_limit_32b,
516d9f6c64eSTakashi Sakamoto 					       GFP_NOWAIT);
517d9f6c64eSTakashi Sakamoto 			} else {
518d9f6c64eSTakashi Sakamoto 				ret = xa_alloc_bh(&client->resource_xa, &index, resource,
519d9f6c64eSTakashi Sakamoto 						  xa_limit_32b, GFP_NOWAIT);
520d9f6c64eSTakashi Sakamoto 			}
521d9f6c64eSTakashi Sakamoto 		}
522e71d31daSStefan Richter 		if (ret >= 0) {
523d9f6c64eSTakashi Sakamoto 			resource->handle = index;
524e71d31daSStefan Richter 			client_get(client);
5256ec9e926STakashi Sakamoto 			if (is_iso_resource(resource))
5266ec9e926STakashi Sakamoto 				schedule_iso_resource(to_iso_resource(resource), 0);
527e71d31daSStefan Richter 		}
528d3816b8bSTakashi Sakamoto 	}
529e71d31daSStefan Richter 
530e71d31daSStefan Richter 	return ret < 0 ? ret : 0;
531e71d31daSStefan Richter }
532e71d31daSStefan Richter 
release_client_resource(struct client * client,u32 handle,client_resource_release_fn_t release,struct client_resource ** return_resource)533e71d31daSStefan Richter static int release_client_resource(struct client *client, u32 handle,
534e71d31daSStefan Richter 				   client_resource_release_fn_t release,
535e21fcf79SStefan Richter 				   struct client_resource **return_resource)
536e71d31daSStefan Richter {
537d9f6c64eSTakashi Sakamoto 	unsigned long index = handle;
538e21fcf79SStefan Richter 	struct client_resource *resource;
539e71d31daSStefan Richter 
540d3816b8bSTakashi Sakamoto 	scoped_guard(spinlock_irq, &client->lock) {
541e71d31daSStefan Richter 		if (client->in_shutdown)
5423b443fe0STakashi Sakamoto 			return -EINVAL;
5433b443fe0STakashi Sakamoto 
544d9f6c64eSTakashi Sakamoto 		resource = xa_load(&client->resource_xa, index);
5453b443fe0STakashi Sakamoto 		if (!resource || resource->release != release)
5463b443fe0STakashi Sakamoto 			return -EINVAL;
5473b443fe0STakashi Sakamoto 
548d9f6c64eSTakashi Sakamoto 		xa_erase(&client->resource_xa, handle);
549d3816b8bSTakashi Sakamoto 	}
550e71d31daSStefan Richter 
551e21fcf79SStefan Richter 	if (return_resource)
552e21fcf79SStefan Richter 		*return_resource = resource;
553e71d31daSStefan Richter 	else
554e21fcf79SStefan Richter 		resource->release(client, resource);
555e71d31daSStefan Richter 
556e71d31daSStefan Richter 	client_put(client);
557e71d31daSStefan Richter 
558e71d31daSStefan Richter 	return 0;
559e71d31daSStefan Richter }
560e71d31daSStefan Richter 
release_transaction(struct client * client,struct client_resource * resource)561e71d31daSStefan Richter static void release_transaction(struct client *client,
562e71d31daSStefan Richter 				struct client_resource *resource)
563e71d31daSStefan Richter {
564e71d31daSStefan Richter }
565e71d31daSStefan Richter 
complete_transaction(struct fw_card * card,int rcode,u32 request_tstamp,u32 response_tstamp,void * payload,size_t length,void * data)566d8527cabSTakashi Sakamoto static void complete_transaction(struct fw_card *card, int rcode, u32 request_tstamp,
567d8527cabSTakashi Sakamoto 				 u32 response_tstamp, void *payload, size_t length, void *data)
568e71d31daSStefan Richter {
569e71d31daSStefan Richter 	struct outbound_transaction_event *e = data;
570e71d31daSStefan Richter 	struct client *client = e->client;
571d9f6c64eSTakashi Sakamoto 	unsigned long index = e->r.resource.handle;
572e71d31daSStefan Richter 
573d3816b8bSTakashi Sakamoto 	scoped_guard(spinlock_irqsave, &client->lock) {
574d9f6c64eSTakashi Sakamoto 		xa_erase(&client->resource_xa, index);
5755a5e62daSClemens Ladisch 		if (client->in_shutdown)
5765a5e62daSClemens Ladisch 			wake_up(&client->tx_flush_wait);
577d3816b8bSTakashi Sakamoto 	}
578e71d31daSStefan Richter 
579d8527cabSTakashi Sakamoto 	switch (e->rsp.without_tstamp.type) {
580d8527cabSTakashi Sakamoto 	case FW_CDEV_EVENT_RESPONSE:
581d8527cabSTakashi Sakamoto 	{
582d8527cabSTakashi Sakamoto 		struct fw_cdev_event_response *rsp = &e->rsp.without_tstamp;
583d8527cabSTakashi Sakamoto 
584d8527cabSTakashi Sakamoto 		if (length < rsp->length)
585d8527cabSTakashi Sakamoto 			rsp->length = length;
586d8527cabSTakashi Sakamoto 		if (rcode == RCODE_COMPLETE)
587d8527cabSTakashi Sakamoto 			memcpy(rsp->data, payload, rsp->length);
588d8527cabSTakashi Sakamoto 
589e71d31daSStefan Richter 		rsp->rcode = rcode;
590e71d31daSStefan Richter 
591d8527cabSTakashi Sakamoto 		// In the case that sizeof(*rsp) doesn't align with the position of the
592d8527cabSTakashi Sakamoto 		// data, and the read is short, preserve an extra copy of the data
593d8527cabSTakashi Sakamoto 		// to stay compatible with a pre-2.6.27 bug.  Since the bug is harmless
594d8527cabSTakashi Sakamoto 		// for short reads and some apps depended on it, this is both safe
595d8527cabSTakashi Sakamoto 		// and prudent for compatibility.
596e71d31daSStefan Richter 		if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data))
597d8527cabSTakashi Sakamoto 			queue_event(client, &e->event, rsp, sizeof(*rsp), rsp->data, rsp->length);
598e71d31daSStefan Richter 		else
599d8527cabSTakashi Sakamoto 			queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0);
600d8527cabSTakashi Sakamoto 
601d8527cabSTakashi Sakamoto 		break;
602d8527cabSTakashi Sakamoto 	}
603d8527cabSTakashi Sakamoto 	case FW_CDEV_EVENT_RESPONSE2:
604d8527cabSTakashi Sakamoto 	{
605d8527cabSTakashi Sakamoto 		struct fw_cdev_event_response2 *rsp = &e->rsp.with_tstamp;
606d8527cabSTakashi Sakamoto 
607d8527cabSTakashi Sakamoto 		if (length < rsp->length)
608d8527cabSTakashi Sakamoto 			rsp->length = length;
609d8527cabSTakashi Sakamoto 		if (rcode == RCODE_COMPLETE)
610d8527cabSTakashi Sakamoto 			memcpy(rsp->data, payload, rsp->length);
611d8527cabSTakashi Sakamoto 
612d8527cabSTakashi Sakamoto 		rsp->rcode = rcode;
613d8527cabSTakashi Sakamoto 		rsp->request_tstamp = request_tstamp;
614d8527cabSTakashi Sakamoto 		rsp->response_tstamp = response_tstamp;
615d8527cabSTakashi Sakamoto 
616d8527cabSTakashi Sakamoto 		queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, NULL, 0);
617d8527cabSTakashi Sakamoto 
618d8527cabSTakashi Sakamoto 		break;
619ebb9d3caSTakashi Sakamoto 	}
620d8527cabSTakashi Sakamoto 	default:
621d8527cabSTakashi Sakamoto 		WARN_ON(1);
622d8527cabSTakashi Sakamoto 		break;
623d8527cabSTakashi Sakamoto 	}
624e71d31daSStefan Richter 
625d9f6c64eSTakashi Sakamoto 	// Drop the xarray's reference.
6265a5e62daSClemens Ladisch 	client_put(client);
627e71d31daSStefan Richter }
628e71d31daSStefan Richter 
init_request(struct client * client,struct fw_cdev_send_request * request,int destination_id,int speed)629e71d31daSStefan Richter static int init_request(struct client *client,
630e71d31daSStefan Richter 			struct fw_cdev_send_request *request,
631e71d31daSStefan Richter 			int destination_id, int speed)
632e71d31daSStefan Richter {
633e71d31daSStefan Richter 	struct outbound_transaction_event *e;
634147e9d3aSTakashi Sakamoto 	void *payload;
635e71d31daSStefan Richter 	int ret;
636e71d31daSStefan Richter 
637e71d31daSStefan Richter 	if (request->tcode != TCODE_STREAM_DATA &&
638e71d31daSStefan Richter 	    (request->length > 4096 || request->length > 512 << speed))
639e71d31daSStefan Richter 		return -EIO;
640e71d31daSStefan Richter 
641a8e93f3dSClemens Ladisch 	if (request->tcode == TCODE_WRITE_QUADLET_REQUEST &&
642a8e93f3dSClemens Ladisch 	    request->length < 4)
643a8e93f3dSClemens Ladisch 		return -EINVAL;
644a8e93f3dSClemens Ladisch 
645e71d31daSStefan Richter 	e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
646e71d31daSStefan Richter 	if (e == NULL)
647e71d31daSStefan Richter 		return -ENOMEM;
648e71d31daSStefan Richter 	e->client = client;
649e71d31daSStefan Richter 
650d8527cabSTakashi Sakamoto 	if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) {
651d8527cabSTakashi Sakamoto 		struct fw_cdev_event_response *rsp = &e->rsp.without_tstamp;
652d8527cabSTakashi Sakamoto 
653d8527cabSTakashi Sakamoto 		rsp->type = FW_CDEV_EVENT_RESPONSE;
654147e9d3aSTakashi Sakamoto 		rsp->length = request->length;
655147e9d3aSTakashi Sakamoto 		rsp->closure = request->closure;
656147e9d3aSTakashi Sakamoto 		payload = rsp->data;
657d8527cabSTakashi Sakamoto 	} else {
658d8527cabSTakashi Sakamoto 		struct fw_cdev_event_response2 *rsp = &e->rsp.with_tstamp;
659d8527cabSTakashi Sakamoto 
660d8527cabSTakashi Sakamoto 		rsp->type = FW_CDEV_EVENT_RESPONSE2;
661d8527cabSTakashi Sakamoto 		rsp->length = request->length;
662d8527cabSTakashi Sakamoto 		rsp->closure = request->closure;
663d8527cabSTakashi Sakamoto 		payload = rsp->data;
664d8527cabSTakashi Sakamoto 	}
665147e9d3aSTakashi Sakamoto 
666147e9d3aSTakashi Sakamoto 	if (request->data && copy_from_user(payload, u64_to_uptr(request->data), request->length)) {
667e71d31daSStefan Richter 		ret = -EFAULT;
668e71d31daSStefan Richter 		goto failed;
669e71d31daSStefan Richter 	}
670e71d31daSStefan Richter 
671e71d31daSStefan Richter 	e->r.resource.release = release_transaction;
672e71d31daSStefan Richter 	ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
673e71d31daSStefan Richter 	if (ret < 0)
674e71d31daSStefan Richter 		goto failed;
675e71d31daSStefan Richter 
676d8527cabSTakashi Sakamoto 	fw_send_request_with_tstamp(client->device->card, &e->r.transaction, request->tcode,
677d8527cabSTakashi Sakamoto 				    destination_id, request->generation, speed, request->offset,
678d8527cabSTakashi Sakamoto 				    payload, request->length, complete_transaction, e);
679e71d31daSStefan Richter 	return 0;
680e71d31daSStefan Richter 
681e71d31daSStefan Richter  failed:
682e71d31daSStefan Richter 	kfree(e);
683e71d31daSStefan Richter 
684e71d31daSStefan Richter 	return ret;
685e71d31daSStefan Richter }
686e71d31daSStefan Richter 
ioctl_send_request(struct client * client,union ioctl_arg * arg)6876e95dea7SStefan Richter static int ioctl_send_request(struct client *client, union ioctl_arg *arg)
688e71d31daSStefan Richter {
6896e95dea7SStefan Richter 	switch (arg->send_request.tcode) {
690e71d31daSStefan Richter 	case TCODE_WRITE_QUADLET_REQUEST:
691e71d31daSStefan Richter 	case TCODE_WRITE_BLOCK_REQUEST:
692e71d31daSStefan Richter 	case TCODE_READ_QUADLET_REQUEST:
693e71d31daSStefan Richter 	case TCODE_READ_BLOCK_REQUEST:
694e71d31daSStefan Richter 	case TCODE_LOCK_MASK_SWAP:
695e71d31daSStefan Richter 	case TCODE_LOCK_COMPARE_SWAP:
696e71d31daSStefan Richter 	case TCODE_LOCK_FETCH_ADD:
697e71d31daSStefan Richter 	case TCODE_LOCK_LITTLE_ADD:
698e71d31daSStefan Richter 	case TCODE_LOCK_BOUNDED_ADD:
699e71d31daSStefan Richter 	case TCODE_LOCK_WRAP_ADD:
700e71d31daSStefan Richter 	case TCODE_LOCK_VENDOR_DEPENDENT:
701e71d31daSStefan Richter 		break;
702e71d31daSStefan Richter 	default:
703e71d31daSStefan Richter 		return -EINVAL;
704e71d31daSStefan Richter 	}
705e71d31daSStefan Richter 
7066e95dea7SStefan Richter 	return init_request(client, &arg->send_request, client->device->node_id,
707e71d31daSStefan Richter 			    client->device->max_speed);
708e71d31daSStefan Richter }
709e71d31daSStefan Richter 
release_request(struct client * client,struct client_resource * resource)710e71d31daSStefan Richter static void release_request(struct client *client,
711e71d31daSStefan Richter 			    struct client_resource *resource)
712e71d31daSStefan Richter {
713ced2da31STakashi Sakamoto 	struct inbound_transaction_resource *r = to_inbound_transaction_resource(resource);
714e71d31daSStefan Richter 
715e6996002STakashi Sakamoto 	if (r->is_fcp)
71639859be8STakashi Sakamoto 		fw_request_put(r->request);
717281e2032SStefan Richter 	else
71808bd34c9SJay Fenlason 		fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR);
7190244f573SStefan Richter 
7200244f573SStefan Richter 	fw_card_put(r->card);
721e71d31daSStefan Richter 	kfree(r);
722e71d31daSStefan Richter }
723e71d31daSStefan Richter 
handle_request(struct fw_card * card,struct fw_request * request,int tcode,int destination,int source,int generation,unsigned long long offset,void * payload,size_t length,void * callback_data)724e71d31daSStefan Richter static void handle_request(struct fw_card *card, struct fw_request *request,
725e71d31daSStefan Richter 			   int tcode, int destination, int source,
72633e553feSStefan Richter 			   int generation, unsigned long long offset,
727e71d31daSStefan Richter 			   void *payload, size_t length, void *callback_data)
728e71d31daSStefan Richter {
729e71d31daSStefan Richter 	struct address_handler_resource *handler = callback_data;
730e6996002STakashi Sakamoto 	bool is_fcp = is_in_fcp_region(offset, length);
731e71d31daSStefan Richter 	struct inbound_transaction_resource *r;
732e71d31daSStefan Richter 	struct inbound_transaction_event *e;
733e205597dSStefan Richter 	size_t event_size0;
734e71d31daSStefan Richter 	int ret;
735e71d31daSStefan Richter 
7360244f573SStefan Richter 	/* card may be different from handler->client->device->card */
7370244f573SStefan Richter 	fw_card_get(card);
7380244f573SStefan Richter 
73939859be8STakashi Sakamoto 	// Extend the lifetime of data for request so that its payload is safely accessible in
74039859be8STakashi Sakamoto 	// the process context for the client.
74139859be8STakashi Sakamoto 	if (is_fcp)
74239859be8STakashi Sakamoto 		fw_request_get(request);
74339859be8STakashi Sakamoto 
744e71d31daSStefan Richter 	r = kmalloc(sizeof(*r), GFP_ATOMIC);
745e71d31daSStefan Richter 	e = kmalloc(sizeof(*e), GFP_ATOMIC);
746cfb0c9d1SStefan Richter 	if (r == NULL || e == NULL)
747e71d31daSStefan Richter 		goto failed;
748cfb0c9d1SStefan Richter 
74908bd34c9SJay Fenlason 	r->card    = card;
750e71d31daSStefan Richter 	r->request = request;
751e6996002STakashi Sakamoto 	r->is_fcp  = is_fcp;
752e71d31daSStefan Richter 	r->data    = payload;
753e71d31daSStefan Richter 	r->length  = length;
754e71d31daSStefan Richter 
755e71d31daSStefan Richter 	r->resource.release = release_request;
756e71d31daSStefan Richter 	ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC);
757e71d31daSStefan Richter 	if (ret < 0)
758e71d31daSStefan Richter 		goto failed;
759e71d31daSStefan Richter 
760e205597dSStefan Richter 	if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) {
761e205597dSStefan Richter 		struct fw_cdev_event_request *req = &e->req.request;
762e205597dSStefan Richter 
763e205597dSStefan Richter 		if (tcode & 0x10)
764e205597dSStefan Richter 			tcode = TCODE_LOCK_REQUEST;
765e205597dSStefan Richter 
766e205597dSStefan Richter 		req->type	= FW_CDEV_EVENT_REQUEST;
767e205597dSStefan Richter 		req->tcode	= tcode;
768e205597dSStefan Richter 		req->offset	= offset;
769e205597dSStefan Richter 		req->length	= length;
770e205597dSStefan Richter 		req->handle	= r->resource.handle;
771e205597dSStefan Richter 		req->closure	= handler->closure;
772e205597dSStefan Richter 		event_size0	= sizeof(*req);
773865efffbSTakashi Sakamoto 	} else if (handler->client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) {
774e205597dSStefan Richter 		struct fw_cdev_event_request2 *req = &e->req.request2;
775e205597dSStefan Richter 
776e205597dSStefan Richter 		req->type	= FW_CDEV_EVENT_REQUEST2;
777e205597dSStefan Richter 		req->tcode	= tcode;
778e205597dSStefan Richter 		req->offset	= offset;
779e205597dSStefan Richter 		req->source_node_id = source;
780e205597dSStefan Richter 		req->destination_node_id = destination;
781e205597dSStefan Richter 		req->card	= card->index;
782e205597dSStefan Richter 		req->generation	= generation;
783e205597dSStefan Richter 		req->length	= length;
784e205597dSStefan Richter 		req->handle	= r->resource.handle;
785e205597dSStefan Richter 		req->closure	= handler->closure;
786e205597dSStefan Richter 		event_size0	= sizeof(*req);
787865efffbSTakashi Sakamoto 	} else {
788865efffbSTakashi Sakamoto 		struct fw_cdev_event_request3 *req = &e->req.with_tstamp;
789865efffbSTakashi Sakamoto 
790865efffbSTakashi Sakamoto 		req->type	= FW_CDEV_EVENT_REQUEST3;
791865efffbSTakashi Sakamoto 		req->tcode	= tcode;
792865efffbSTakashi Sakamoto 		req->offset	= offset;
793865efffbSTakashi Sakamoto 		req->source_node_id = source;
794865efffbSTakashi Sakamoto 		req->destination_node_id = destination;
795865efffbSTakashi Sakamoto 		req->card	= card->index;
796865efffbSTakashi Sakamoto 		req->generation	= generation;
797865efffbSTakashi Sakamoto 		req->length	= length;
798865efffbSTakashi Sakamoto 		req->handle	= r->resource.handle;
799865efffbSTakashi Sakamoto 		req->closure	= handler->closure;
800865efffbSTakashi Sakamoto 		req->tstamp	= fw_request_get_timestamp(request);
801865efffbSTakashi Sakamoto 		event_size0	= sizeof(*req);
802e205597dSStefan Richter 	}
803e71d31daSStefan Richter 
804e71d31daSStefan Richter 	queue_event(handler->client, &e->event,
805e205597dSStefan Richter 		    &e->req, event_size0, r->data, length);
806e71d31daSStefan Richter 	return;
807e71d31daSStefan Richter 
808e71d31daSStefan Richter  failed:
809e71d31daSStefan Richter 	kfree(r);
810e71d31daSStefan Richter 	kfree(e);
811281e2032SStefan Richter 
812e6996002STakashi Sakamoto 	if (!is_fcp)
813e71d31daSStefan Richter 		fw_send_response(card, request, RCODE_CONFLICT_ERROR);
81439859be8STakashi Sakamoto 	else
81539859be8STakashi Sakamoto 		fw_request_put(request);
8160244f573SStefan Richter 
8170244f573SStefan Richter 	fw_card_put(card);
818e71d31daSStefan Richter }
819e71d31daSStefan Richter 
release_address_handler(struct client * client,struct client_resource * resource)820e71d31daSStefan Richter static void release_address_handler(struct client *client,
821e71d31daSStefan Richter 				    struct client_resource *resource)
822e71d31daSStefan Richter {
823ced2da31STakashi Sakamoto 	struct address_handler_resource *r = to_address_handler_resource(resource);
824e71d31daSStefan Richter 
825e71d31daSStefan Richter 	fw_core_remove_address_handler(&r->handler);
826e71d31daSStefan Richter 	kfree(r);
827e71d31daSStefan Richter }
828e71d31daSStefan Richter 
ioctl_allocate(struct client * client,union ioctl_arg * arg)8296e95dea7SStefan Richter static int ioctl_allocate(struct client *client, union ioctl_arg *arg)
830e71d31daSStefan Richter {
8316e95dea7SStefan Richter 	struct fw_cdev_allocate *a = &arg->allocate;
832e71d31daSStefan Richter 	struct address_handler_resource *r;
833e71d31daSStefan Richter 	struct fw_address_region region;
834e71d31daSStefan Richter 	int ret;
835e71d31daSStefan Richter 
836e71d31daSStefan Richter 	r = kmalloc(sizeof(*r), GFP_KERNEL);
837e71d31daSStefan Richter 	if (r == NULL)
838e71d31daSStefan Richter 		return -ENOMEM;
839e71d31daSStefan Richter 
8406e95dea7SStefan Richter 	region.start = a->offset;
8418e2b2b46SStefan Richter 	if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END)
8426e95dea7SStefan Richter 		region.end = a->offset + a->length;
8438e2b2b46SStefan Richter 	else
8448e2b2b46SStefan Richter 		region.end = a->region_end;
8458e2b2b46SStefan Richter 
8466e95dea7SStefan Richter 	r->handler.length           = a->length;
847e71d31daSStefan Richter 	r->handler.address_callback = handle_request;
848e71d31daSStefan Richter 	r->handler.callback_data    = r;
8496e95dea7SStefan Richter 	r->closure   = a->closure;
850e71d31daSStefan Richter 	r->client    = client;
851e71d31daSStefan Richter 
852e71d31daSStefan Richter 	ret = fw_core_add_address_handler(&r->handler, &region);
853e71d31daSStefan Richter 	if (ret < 0) {
854e71d31daSStefan Richter 		kfree(r);
855e71d31daSStefan Richter 		return ret;
856e71d31daSStefan Richter 	}
8578e2b2b46SStefan Richter 	a->offset = r->handler.offset;
858e71d31daSStefan Richter 
859e71d31daSStefan Richter 	r->resource.release = release_address_handler;
860e71d31daSStefan Richter 	ret = add_client_resource(client, &r->resource, GFP_KERNEL);
861e71d31daSStefan Richter 	if (ret < 0) {
862e71d31daSStefan Richter 		release_address_handler(client, &r->resource);
863e71d31daSStefan Richter 		return ret;
864e71d31daSStefan Richter 	}
8656e95dea7SStefan Richter 	a->handle = r->resource.handle;
866e71d31daSStefan Richter 
867e71d31daSStefan Richter 	return 0;
868e71d31daSStefan Richter }
869e71d31daSStefan Richter 
ioctl_deallocate(struct client * client,union ioctl_arg * arg)8706e95dea7SStefan Richter static int ioctl_deallocate(struct client *client, union ioctl_arg *arg)
871e71d31daSStefan Richter {
8726e95dea7SStefan Richter 	return release_client_resource(client, arg->deallocate.handle,
873e71d31daSStefan Richter 				       release_address_handler, NULL);
874e71d31daSStefan Richter }
875e71d31daSStefan Richter 
ioctl_send_response(struct client * client,union ioctl_arg * arg)8766e95dea7SStefan Richter static int ioctl_send_response(struct client *client, union ioctl_arg *arg)
877e71d31daSStefan Richter {
8786e95dea7SStefan Richter 	struct fw_cdev_send_response *a = &arg->send_response;
879e71d31daSStefan Richter 	struct client_resource *resource;
880e71d31daSStefan Richter 	struct inbound_transaction_resource *r;
8817e44c0b5SStefan Richter 	int ret = 0;
882e71d31daSStefan Richter 
8836e95dea7SStefan Richter 	if (release_client_resource(client, a->handle,
884e71d31daSStefan Richter 				    release_request, &resource) < 0)
885e71d31daSStefan Richter 		return -EINVAL;
886e71d31daSStefan Richter 
887ced2da31STakashi Sakamoto 	r = to_inbound_transaction_resource(resource);
888e6996002STakashi Sakamoto 	if (r->is_fcp) {
88939859be8STakashi Sakamoto 		fw_request_put(r->request);
890281e2032SStefan Richter 		goto out;
891531390a2STakashi Sakamoto 	}
892281e2032SStefan Richter 
893a10c0ce7SClemens Ladisch 	if (a->length != fw_get_response_length(r->request)) {
894a10c0ce7SClemens Ladisch 		ret = -EINVAL;
89513a55d6bSTakashi Sakamoto 		fw_request_put(r->request);
896a10c0ce7SClemens Ladisch 		goto out;
897a10c0ce7SClemens Ladisch 	}
898a10c0ce7SClemens Ladisch 	if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) {
8997e44c0b5SStefan Richter 		ret = -EFAULT;
90013a55d6bSTakashi Sakamoto 		fw_request_put(r->request);
9017e44c0b5SStefan Richter 		goto out;
9027e44c0b5SStefan Richter 	}
90308bd34c9SJay Fenlason 	fw_send_response(r->card, r->request, a->rcode);
9047e44c0b5SStefan Richter  out:
9050244f573SStefan Richter 	fw_card_put(r->card);
906e71d31daSStefan Richter 	kfree(r);
907e71d31daSStefan Richter 
9087e44c0b5SStefan Richter 	return ret;
909e71d31daSStefan Richter }
910e71d31daSStefan Richter 
ioctl_initiate_bus_reset(struct client * client,union ioctl_arg * arg)9116e95dea7SStefan Richter static int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg)
912e71d31daSStefan Richter {
91302d37bedSStefan Richter 	fw_schedule_bus_reset(client->device->card, true,
9146e95dea7SStefan Richter 			arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET);
91502d37bedSStefan Richter 	return 0;
916e71d31daSStefan Richter }
917e71d31daSStefan Richter 
release_descriptor(struct client * client,struct client_resource * resource)918e71d31daSStefan Richter static void release_descriptor(struct client *client,
919e71d31daSStefan Richter 			       struct client_resource *resource)
920e71d31daSStefan Richter {
921ced2da31STakashi Sakamoto 	struct descriptor_resource *r = to_descriptor_resource(resource);
922e71d31daSStefan Richter 
923e71d31daSStefan Richter 	fw_core_remove_descriptor(&r->descriptor);
924e71d31daSStefan Richter 	kfree(r);
925e71d31daSStefan Richter }
926e71d31daSStefan Richter 
ioctl_add_descriptor(struct client * client,union ioctl_arg * arg)9276e95dea7SStefan Richter static int ioctl_add_descriptor(struct client *client, union ioctl_arg *arg)
928e71d31daSStefan Richter {
9296e95dea7SStefan Richter 	struct fw_cdev_add_descriptor *a = &arg->add_descriptor;
930e71d31daSStefan Richter 	struct descriptor_resource *r;
931e71d31daSStefan Richter 	int ret;
932e71d31daSStefan Richter 
933e71d31daSStefan Richter 	/* Access policy: Allow this ioctl only on local nodes' device files. */
934e71d31daSStefan Richter 	if (!client->device->is_local)
935e71d31daSStefan Richter 		return -ENOSYS;
936e71d31daSStefan Richter 
9376e95dea7SStefan Richter 	if (a->length > 256)
938e71d31daSStefan Richter 		return -EINVAL;
939e71d31daSStefan Richter 
9406e95dea7SStefan Richter 	r = kmalloc(sizeof(*r) + a->length * 4, GFP_KERNEL);
941e71d31daSStefan Richter 	if (r == NULL)
942e71d31daSStefan Richter 		return -ENOMEM;
943e71d31daSStefan Richter 
9446e95dea7SStefan Richter 	if (copy_from_user(r->data, u64_to_uptr(a->data), a->length * 4)) {
945e71d31daSStefan Richter 		ret = -EFAULT;
946e71d31daSStefan Richter 		goto failed;
947e71d31daSStefan Richter 	}
948e71d31daSStefan Richter 
9496e95dea7SStefan Richter 	r->descriptor.length    = a->length;
9506e95dea7SStefan Richter 	r->descriptor.immediate = a->immediate;
9516e95dea7SStefan Richter 	r->descriptor.key       = a->key;
952e71d31daSStefan Richter 	r->descriptor.data      = r->data;
953e71d31daSStefan Richter 
954e71d31daSStefan Richter 	ret = fw_core_add_descriptor(&r->descriptor);
955e71d31daSStefan Richter 	if (ret < 0)
956e71d31daSStefan Richter 		goto failed;
957e71d31daSStefan Richter 
958e71d31daSStefan Richter 	r->resource.release = release_descriptor;
959e71d31daSStefan Richter 	ret = add_client_resource(client, &r->resource, GFP_KERNEL);
960e71d31daSStefan Richter 	if (ret < 0) {
961e71d31daSStefan Richter 		fw_core_remove_descriptor(&r->descriptor);
962e71d31daSStefan Richter 		goto failed;
963e71d31daSStefan Richter 	}
9646e95dea7SStefan Richter 	a->handle = r->resource.handle;
965e71d31daSStefan Richter 
966e71d31daSStefan Richter 	return 0;
967e71d31daSStefan Richter  failed:
968e71d31daSStefan Richter 	kfree(r);
969e71d31daSStefan Richter 
970e71d31daSStefan Richter 	return ret;
971e71d31daSStefan Richter }
972e71d31daSStefan Richter 
ioctl_remove_descriptor(struct client * client,union ioctl_arg * arg)9736e95dea7SStefan Richter static int ioctl_remove_descriptor(struct client *client, union ioctl_arg *arg)
974e71d31daSStefan Richter {
9756e95dea7SStefan Richter 	return release_client_resource(client, arg->remove_descriptor.handle,
976e71d31daSStefan Richter 				       release_descriptor, NULL);
977e71d31daSStefan Richter }
978e71d31daSStefan Richter 
iso_callback(struct fw_iso_context * context,u32 cycle,size_t header_length,void * header,void * data)979e71d31daSStefan Richter static void iso_callback(struct fw_iso_context *context, u32 cycle,
980e71d31daSStefan Richter 			 size_t header_length, void *header, void *data)
981e71d31daSStefan Richter {
982e71d31daSStefan Richter 	struct client *client = data;
983e71d31daSStefan Richter 	struct iso_interrupt_event *e;
984e71d31daSStefan Richter 
985f62ec13eSTakashi Sakamoto 	e = kmalloc(sizeof(*e) + header_length, GFP_KERNEL);
986cfb0c9d1SStefan Richter 	if (e == NULL)
987e71d31daSStefan Richter 		return;
988cfb0c9d1SStefan Richter 
989e71d31daSStefan Richter 	e->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
990e71d31daSStefan Richter 	e->interrupt.closure   = client->iso_closure;
991e71d31daSStefan Richter 	e->interrupt.cycle     = cycle;
992e71d31daSStefan Richter 	e->interrupt.header_length = header_length;
993e71d31daSStefan Richter 	memcpy(e->interrupt.header, header, header_length);
994e71d31daSStefan Richter 	queue_event(client, &e->event, &e->interrupt,
995e71d31daSStefan Richter 		    sizeof(e->interrupt) + header_length, NULL, 0);
996e71d31daSStefan Richter }
997e71d31daSStefan Richter 
iso_mc_callback(struct fw_iso_context * context,dma_addr_t completed,void * data)998872e330eSStefan Richter static void iso_mc_callback(struct fw_iso_context *context,
999872e330eSStefan Richter 			    dma_addr_t completed, void *data)
1000872e330eSStefan Richter {
1001872e330eSStefan Richter 	struct client *client = data;
1002872e330eSStefan Richter 	struct iso_interrupt_mc_event *e;
1003872e330eSStefan Richter 
1004f62ec13eSTakashi Sakamoto 	e = kmalloc(sizeof(*e), GFP_KERNEL);
1005cfb0c9d1SStefan Richter 	if (e == NULL)
1006872e330eSStefan Richter 		return;
1007cfb0c9d1SStefan Richter 
1008872e330eSStefan Richter 	e->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL;
1009872e330eSStefan Richter 	e->interrupt.closure   = client->iso_closure;
1010872e330eSStefan Richter 	e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer,
1011872e330eSStefan Richter 						      completed);
1012872e330eSStefan Richter 	queue_event(client, &e->event, &e->interrupt,
1013872e330eSStefan Richter 		    sizeof(e->interrupt), NULL, 0);
1014872e330eSStefan Richter }
1015872e330eSStefan Richter 
iso_dma_direction(struct fw_iso_context * context)10160b6c4857SStefan Richter static enum dma_data_direction iso_dma_direction(struct fw_iso_context *context)
10170b6c4857SStefan Richter {
10180b6c4857SStefan Richter 		if (context->type == FW_ISO_CONTEXT_TRANSMIT)
10190b6c4857SStefan Richter 			return DMA_TO_DEVICE;
10200b6c4857SStefan Richter 		else
10210b6c4857SStefan Richter 			return DMA_FROM_DEVICE;
10220b6c4857SStefan Richter }
10230b6c4857SStefan Richter 
fw_iso_mc_context_create(struct fw_card * card,fw_iso_mc_callback_t callback,void * callback_data)1024ebe4560eSOscar Carter static struct fw_iso_context *fw_iso_mc_context_create(struct fw_card *card,
1025ebe4560eSOscar Carter 						fw_iso_mc_callback_t callback,
1026ebe4560eSOscar Carter 						void *callback_data)
1027ebe4560eSOscar Carter {
1028ebe4560eSOscar Carter 	struct fw_iso_context *ctx;
1029ebe4560eSOscar Carter 
1030ebe4560eSOscar Carter 	ctx = fw_iso_context_create(card, FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL,
1031ebe4560eSOscar Carter 				    0, 0, 0, NULL, callback_data);
1032ebe4560eSOscar Carter 	if (!IS_ERR(ctx))
1033ebe4560eSOscar Carter 		ctx->callback.mc = callback;
1034ebe4560eSOscar Carter 
1035ebe4560eSOscar Carter 	return ctx;
1036ebe4560eSOscar Carter }
1037ebe4560eSOscar Carter 
ioctl_create_iso_context(struct client * client,union ioctl_arg * arg)10386e95dea7SStefan Richter static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
1039e71d31daSStefan Richter {
10406e95dea7SStefan Richter 	struct fw_cdev_create_iso_context *a = &arg->create_iso_context;
1041e71d31daSStefan Richter 	struct fw_iso_context *context;
1042ebe4560eSOscar Carter 	union fw_iso_callback cb;
10430b6c4857SStefan Richter 	int ret;
1044e71d31daSStefan Richter 
1045eb5b35a5SStefan Richter 	BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
1046872e330eSStefan Richter 		     FW_CDEV_ISO_CONTEXT_RECEIVE  != FW_ISO_CONTEXT_RECEIVE  ||
1047872e330eSStefan Richter 		     FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL !=
1048872e330eSStefan Richter 					FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL);
1049e71d31daSStefan Richter 
10506e95dea7SStefan Richter 	switch (a->type) {
1051872e330eSStefan Richter 	case FW_ISO_CONTEXT_TRANSMIT:
1052872e330eSStefan Richter 		if (a->speed > SCODE_3200 || a->channel > 63)
1053e71d31daSStefan Richter 			return -EINVAL;
1054872e330eSStefan Richter 
1055ebe4560eSOscar Carter 		cb.sc = iso_callback;
1056e71d31daSStefan Richter 		break;
1057e71d31daSStefan Richter 
1058872e330eSStefan Richter 	case FW_ISO_CONTEXT_RECEIVE:
1059872e330eSStefan Richter 		if (a->header_size < 4 || (a->header_size & 3) ||
1060872e330eSStefan Richter 		    a->channel > 63)
1061e71d31daSStefan Richter 			return -EINVAL;
1062872e330eSStefan Richter 
1063ebe4560eSOscar Carter 		cb.sc = iso_callback;
1064872e330eSStefan Richter 		break;
1065872e330eSStefan Richter 
1066872e330eSStefan Richter 	case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
1067ebe4560eSOscar Carter 		cb.mc = iso_mc_callback;
1068e71d31daSStefan Richter 		break;
1069e71d31daSStefan Richter 
1070e71d31daSStefan Richter 	default:
1071e71d31daSStefan Richter 		return -EINVAL;
1072e71d31daSStefan Richter 	}
1073e71d31daSStefan Richter 
1074ebe4560eSOscar Carter 	if (a->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL)
1075ebe4560eSOscar Carter 		context = fw_iso_mc_context_create(client->device->card, cb.mc,
1076ebe4560eSOscar Carter 						   client);
1077ebe4560eSOscar Carter 	else
10786e95dea7SStefan Richter 		context = fw_iso_context_create(client->device->card, a->type,
1079ebe4560eSOscar Carter 						a->channel, a->speed,
1080ebe4560eSOscar Carter 						a->header_size, cb.sc, client);
1081e71d31daSStefan Richter 	if (IS_ERR(context))
1082e71d31daSStefan Richter 		return PTR_ERR(context);
10830699a73aSClemens Ladisch 	if (client->version < FW_CDEV_VERSION_AUTO_FLUSH_ISO_OVERFLOW)
10840699a73aSClemens Ladisch 		context->drop_overflow_headers = true;
1085e71d31daSStefan Richter 
1086cf123b01STakashi Sakamoto 	// We only support one context at this time.
1087cf123b01STakashi Sakamoto 	guard(spinlock_irq)(&client->lock);
1088cf123b01STakashi Sakamoto 
1089bdfe273eSClemens Ladisch 	if (client->iso_context != NULL) {
1090bdfe273eSClemens Ladisch 		fw_iso_context_destroy(context);
10910b6c4857SStefan Richter 
1092bdfe273eSClemens Ladisch 		return -EBUSY;
1093bdfe273eSClemens Ladisch 	}
10940b6c4857SStefan Richter 	if (!client->buffer_is_mapped) {
10950b6c4857SStefan Richter 		ret = fw_iso_buffer_map_dma(&client->buffer,
10960b6c4857SStefan Richter 					    client->device->card,
10970b6c4857SStefan Richter 					    iso_dma_direction(context));
10980b6c4857SStefan Richter 		if (ret < 0) {
10990b6c4857SStefan Richter 			fw_iso_context_destroy(context);
11000b6c4857SStefan Richter 
11010b6c4857SStefan Richter 			return ret;
11020b6c4857SStefan Richter 		}
11030b6c4857SStefan Richter 		client->buffer_is_mapped = true;
11040b6c4857SStefan Richter 	}
11056e95dea7SStefan Richter 	client->iso_closure = a->closure;
1106e71d31daSStefan Richter 	client->iso_context = context;
1107e71d31daSStefan Richter 
11086e95dea7SStefan Richter 	a->handle = 0;
1109e71d31daSStefan Richter 
1110e71d31daSStefan Richter 	return 0;
1111e71d31daSStefan Richter }
1112e71d31daSStefan Richter 
ioctl_set_iso_channels(struct client * client,union ioctl_arg * arg)1113872e330eSStefan Richter static int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg)
1114872e330eSStefan Richter {
1115872e330eSStefan Richter 	struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels;
1116872e330eSStefan Richter 	struct fw_iso_context *ctx = client->iso_context;
1117872e330eSStefan Richter 
1118872e330eSStefan Richter 	if (ctx == NULL || a->handle != 0)
1119872e330eSStefan Richter 		return -EINVAL;
1120872e330eSStefan Richter 
1121872e330eSStefan Richter 	return fw_iso_context_set_channels(ctx, &a->channels);
1122872e330eSStefan Richter }
1123872e330eSStefan Richter 
1124e71d31daSStefan Richter /* Macros for decoding the iso packet control header. */
1125e71d31daSStefan Richter #define GET_PAYLOAD_LENGTH(v)	((v) & 0xffff)
1126e71d31daSStefan Richter #define GET_INTERRUPT(v)	(((v) >> 16) & 0x01)
1127e71d31daSStefan Richter #define GET_SKIP(v)		(((v) >> 17) & 0x01)
1128e71d31daSStefan Richter #define GET_TAG(v)		(((v) >> 18) & 0x03)
1129e71d31daSStefan Richter #define GET_SY(v)		(((v) >> 20) & 0x0f)
1130e71d31daSStefan Richter #define GET_HEADER_LENGTH(v)	(((v) >> 24) & 0xff)
1131e71d31daSStefan Richter 
ioctl_queue_iso(struct client * client,union ioctl_arg * arg)11326e95dea7SStefan Richter static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
1133e71d31daSStefan Richter {
11346e95dea7SStefan Richter 	struct fw_cdev_queue_iso *a = &arg->queue_iso;
1135e71d31daSStefan Richter 	struct fw_cdev_iso_packet __user *p, *end, *next;
1136e71d31daSStefan Richter 	struct fw_iso_context *ctx = client->iso_context;
1137872e330eSStefan Richter 	unsigned long payload, buffer_end, transmit_header_bytes = 0;
1138e71d31daSStefan Richter 	u32 control;
1139e71d31daSStefan Richter 	int count;
1140*ca2c7365SGustavo A. R. Silva 	DEFINE_RAW_FLEX(struct fw_iso_packet, u, header, 64);
1141e71d31daSStefan Richter 
11426e95dea7SStefan Richter 	if (ctx == NULL || a->handle != 0)
1143e71d31daSStefan Richter 		return -EINVAL;
1144e71d31daSStefan Richter 
1145e71d31daSStefan Richter 	/*
1146e71d31daSStefan Richter 	 * If the user passes a non-NULL data pointer, has mmap()'ed
1147e71d31daSStefan Richter 	 * the iso buffer, and the pointer points inside the buffer,
1148e71d31daSStefan Richter 	 * we setup the payload pointers accordingly.  Otherwise we
1149e71d31daSStefan Richter 	 * set them both to 0, which will still let packets with
1150e71d31daSStefan Richter 	 * payload_length == 0 through.  In other words, if no packets
1151e71d31daSStefan Richter 	 * use the indirect payload, the iso buffer need not be mapped
11526e95dea7SStefan Richter 	 * and the a->data pointer is ignored.
1153e71d31daSStefan Richter 	 */
11546e95dea7SStefan Richter 	payload = (unsigned long)a->data - client->vm_start;
1155e71d31daSStefan Richter 	buffer_end = client->buffer.page_count << PAGE_SHIFT;
11566e95dea7SStefan Richter 	if (a->data == 0 || client->buffer.pages == NULL ||
1157e71d31daSStefan Richter 	    payload >= buffer_end) {
1158e71d31daSStefan Richter 		payload = 0;
1159e71d31daSStefan Richter 		buffer_end = 0;
1160e71d31daSStefan Richter 	}
1161e71d31daSStefan Richter 
1162872e330eSStefan Richter 	if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3)
1163872e330eSStefan Richter 		return -EINVAL;
1164e71d31daSStefan Richter 
1165872e330eSStefan Richter 	p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets);
1166e71d31daSStefan Richter 
11676e95dea7SStefan Richter 	end = (void __user *)p + a->size;
1168e71d31daSStefan Richter 	count = 0;
1169e71d31daSStefan Richter 	while (p < end) {
1170e71d31daSStefan Richter 		if (get_user(control, &p->control))
1171e71d31daSStefan Richter 			return -EFAULT;
1172*ca2c7365SGustavo A. R. Silva 		u->payload_length = GET_PAYLOAD_LENGTH(control);
1173*ca2c7365SGustavo A. R. Silva 		u->interrupt = GET_INTERRUPT(control);
1174*ca2c7365SGustavo A. R. Silva 		u->skip = GET_SKIP(control);
1175*ca2c7365SGustavo A. R. Silva 		u->tag = GET_TAG(control);
1176*ca2c7365SGustavo A. R. Silva 		u->sy = GET_SY(control);
1177*ca2c7365SGustavo A. R. Silva 		u->header_length = GET_HEADER_LENGTH(control);
1178e71d31daSStefan Richter 
1179872e330eSStefan Richter 		switch (ctx->type) {
1180872e330eSStefan Richter 		case FW_ISO_CONTEXT_TRANSMIT:
1181*ca2c7365SGustavo A. R. Silva 			if (u->header_length & 3)
1182385ab5bcSClemens Ladisch 				return -EINVAL;
1183*ca2c7365SGustavo A. R. Silva 			transmit_header_bytes = u->header_length;
1184872e330eSStefan Richter 			break;
1185872e330eSStefan Richter 
1186872e330eSStefan Richter 		case FW_ISO_CONTEXT_RECEIVE:
1187*ca2c7365SGustavo A. R. Silva 			if (u->header_length == 0 ||
1188*ca2c7365SGustavo A. R. Silva 			    u->header_length % ctx->header_size != 0)
1189e71d31daSStefan Richter 				return -EINVAL;
1190872e330eSStefan Richter 			break;
1191872e330eSStefan Richter 
1192872e330eSStefan Richter 		case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
1193*ca2c7365SGustavo A. R. Silva 			if (u->payload_length == 0 ||
1194*ca2c7365SGustavo A. R. Silva 			    u->payload_length & 3)
1195e71d31daSStefan Richter 				return -EINVAL;
1196872e330eSStefan Richter 			break;
1197e71d31daSStefan Richter 		}
1198e71d31daSStefan Richter 
1199e71d31daSStefan Richter 		next = (struct fw_cdev_iso_packet __user *)
1200ae2a9766SStefan Richter 			&p->header[transmit_header_bytes / 4];
1201e71d31daSStefan Richter 		if (next > end)
1202e71d31daSStefan Richter 			return -EINVAL;
1203daa98831SAl Viro 		if (copy_from_user
1204*ca2c7365SGustavo A. R. Silva 		    (u->header, p->header, transmit_header_bytes))
1205e71d31daSStefan Richter 			return -EFAULT;
1206*ca2c7365SGustavo A. R. Silva 		if (u->skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
1207*ca2c7365SGustavo A. R. Silva 		    u->header_length + u->payload_length > 0)
1208e71d31daSStefan Richter 			return -EINVAL;
1209*ca2c7365SGustavo A. R. Silva 		if (payload + u->payload_length > buffer_end)
1210e71d31daSStefan Richter 			return -EINVAL;
1211e71d31daSStefan Richter 
1212*ca2c7365SGustavo A. R. Silva 		if (fw_iso_context_queue(ctx, u, &client->buffer, payload))
1213e71d31daSStefan Richter 			break;
1214e71d31daSStefan Richter 
1215e71d31daSStefan Richter 		p = next;
1216*ca2c7365SGustavo A. R. Silva 		payload += u->payload_length;
1217e71d31daSStefan Richter 		count++;
1218e71d31daSStefan Richter 	}
121913882a82SClemens Ladisch 	fw_iso_context_queue_flush(ctx);
1220e71d31daSStefan Richter 
12216e95dea7SStefan Richter 	a->size    -= uptr_to_u64(p) - a->packets;
12226e95dea7SStefan Richter 	a->packets  = uptr_to_u64(p);
12236e95dea7SStefan Richter 	a->data     = client->vm_start + payload;
1224e71d31daSStefan Richter 
1225e71d31daSStefan Richter 	return count;
1226e71d31daSStefan Richter }
1227e71d31daSStefan Richter 
ioctl_start_iso(struct client * client,union ioctl_arg * arg)12286e95dea7SStefan Richter static int ioctl_start_iso(struct client *client, union ioctl_arg *arg)
1229e71d31daSStefan Richter {
12306e95dea7SStefan Richter 	struct fw_cdev_start_iso *a = &arg->start_iso;
1231e71d31daSStefan Richter 
1232eb5b35a5SStefan Richter 	BUILD_BUG_ON(
1233eb5b35a5SStefan Richter 	    FW_CDEV_ISO_CONTEXT_MATCH_TAG0 != FW_ISO_CONTEXT_MATCH_TAG0 ||
1234eb5b35a5SStefan Richter 	    FW_CDEV_ISO_CONTEXT_MATCH_TAG1 != FW_ISO_CONTEXT_MATCH_TAG1 ||
1235eb5b35a5SStefan Richter 	    FW_CDEV_ISO_CONTEXT_MATCH_TAG2 != FW_ISO_CONTEXT_MATCH_TAG2 ||
1236eb5b35a5SStefan Richter 	    FW_CDEV_ISO_CONTEXT_MATCH_TAG3 != FW_ISO_CONTEXT_MATCH_TAG3 ||
1237eb5b35a5SStefan Richter 	    FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS != FW_ISO_CONTEXT_MATCH_ALL_TAGS);
1238eb5b35a5SStefan Richter 
12396e95dea7SStefan Richter 	if (client->iso_context == NULL || a->handle != 0)
1240e71d31daSStefan Richter 		return -EINVAL;
1241e71d31daSStefan Richter 
12426e95dea7SStefan Richter 	if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE &&
12436e95dea7SStefan Richter 	    (a->tags == 0 || a->tags > 15 || a->sync > 15))
1244e71d31daSStefan Richter 		return -EINVAL;
1245e71d31daSStefan Richter 
12466e95dea7SStefan Richter 	return fw_iso_context_start(client->iso_context,
12476e95dea7SStefan Richter 				    a->cycle, a->sync, a->tags);
1248e71d31daSStefan Richter }
1249e71d31daSStefan Richter 
ioctl_stop_iso(struct client * client,union ioctl_arg * arg)12506e95dea7SStefan Richter static int ioctl_stop_iso(struct client *client, union ioctl_arg *arg)
1251e71d31daSStefan Richter {
12526e95dea7SStefan Richter 	struct fw_cdev_stop_iso *a = &arg->stop_iso;
1253e71d31daSStefan Richter 
12546e95dea7SStefan Richter 	if (client->iso_context == NULL || a->handle != 0)
1255e71d31daSStefan Richter 		return -EINVAL;
1256e71d31daSStefan Richter 
1257e71d31daSStefan Richter 	return fw_iso_context_stop(client->iso_context);
1258e71d31daSStefan Richter }
1259e71d31daSStefan Richter 
ioctl_flush_iso(struct client * client,union ioctl_arg * arg)1260d1bbd209SClemens Ladisch static int ioctl_flush_iso(struct client *client, union ioctl_arg *arg)
1261d1bbd209SClemens Ladisch {
1262d1bbd209SClemens Ladisch 	struct fw_cdev_flush_iso *a = &arg->flush_iso;
1263d1bbd209SClemens Ladisch 
1264d1bbd209SClemens Ladisch 	if (client->iso_context == NULL || a->handle != 0)
1265d1bbd209SClemens Ladisch 		return -EINVAL;
1266d1bbd209SClemens Ladisch 
1267d1bbd209SClemens Ladisch 	return fw_iso_context_flush_completions(client->iso_context);
1268d1bbd209SClemens Ladisch }
1269d1bbd209SClemens Ladisch 
ioctl_get_cycle_timer2(struct client * client,union ioctl_arg * arg)12706e95dea7SStefan Richter static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg)
1271e71d31daSStefan Richter {
12726e95dea7SStefan Richter 	struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2;
1273e71d31daSStefan Richter 	struct fw_card *card = client->device->card;
12742c1bb29aSArnd Bergmann 	struct timespec64 ts = {0, 0};
1275dda8ad0aSTakashi Sakamoto 	u32 cycle_time = 0;
1276bacf921cSTakashi Sakamoto 	int ret;
1277e71d31daSStefan Richter 
1278bacf921cSTakashi Sakamoto 	guard(irq)();
1279e71d31daSStefan Richter 
1280baa914cdSTakashi Sakamoto 	ret = fw_card_read_cycle_time(card, &cycle_time);
1281baa914cdSTakashi Sakamoto 	if (ret < 0)
1282bacf921cSTakashi Sakamoto 		return ret;
1283abfe5a01SStefan Richter 
12846e95dea7SStefan Richter 	switch (a->clk_id) {
12852c1bb29aSArnd Bergmann 	case CLOCK_REALTIME:      ktime_get_real_ts64(&ts);	break;
12862c1bb29aSArnd Bergmann 	case CLOCK_MONOTONIC:     ktime_get_ts64(&ts);		break;
12872c1bb29aSArnd Bergmann 	case CLOCK_MONOTONIC_RAW: ktime_get_raw_ts64(&ts);	break;
1288abfe5a01SStefan Richter 	default:
1289bacf921cSTakashi Sakamoto 		return -EINVAL;
1290abfe5a01SStefan Richter 	}
1291e71d31daSStefan Richter 
12926e95dea7SStefan Richter 	a->tv_sec      = ts.tv_sec;
12936e95dea7SStefan Richter 	a->tv_nsec     = ts.tv_nsec;
12946e95dea7SStefan Richter 	a->cycle_timer = cycle_time;
12954a9bde9bSStefan Richter 
1296bacf921cSTakashi Sakamoto 	return 0;
1297abfe5a01SStefan Richter }
1298abfe5a01SStefan Richter 
ioctl_get_cycle_timer(struct client * client,union ioctl_arg * arg)12996e95dea7SStefan Richter static int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg)
1300abfe5a01SStefan Richter {
13016e95dea7SStefan Richter 	struct fw_cdev_get_cycle_timer *a = &arg->get_cycle_timer;
1302abfe5a01SStefan Richter 	struct fw_cdev_get_cycle_timer2 ct2;
1303abfe5a01SStefan Richter 
1304abfe5a01SStefan Richter 	ct2.clk_id = CLOCK_REALTIME;
13056e95dea7SStefan Richter 	ioctl_get_cycle_timer2(client, (union ioctl_arg *)&ct2);
1306abfe5a01SStefan Richter 
13076e95dea7SStefan Richter 	a->local_time = ct2.tv_sec * USEC_PER_SEC + ct2.tv_nsec / NSEC_PER_USEC;
13086e95dea7SStefan Richter 	a->cycle_timer = ct2.cycle_timer;
1309abfe5a01SStefan Richter 
1310e71d31daSStefan Richter 	return 0;
1311e71d31daSStefan Richter }
1312e71d31daSStefan Richter 
iso_resource_work(struct work_struct * work)1313e71d31daSStefan Richter static void iso_resource_work(struct work_struct *work)
1314e71d31daSStefan Richter {
1315e71d31daSStefan Richter 	struct iso_resource_event *e;
1316e71d31daSStefan Richter 	struct iso_resource *r =
1317e71d31daSStefan Richter 			container_of(work, struct iso_resource, work.work);
1318e71d31daSStefan Richter 	struct client *client = r->client;
1319d9f6c64eSTakashi Sakamoto 	unsigned long index = r->resource.handle;
1320e71d31daSStefan Richter 	int generation, channel, bandwidth, todo;
1321e71d31daSStefan Richter 	bool skip, free, success;
1322e71d31daSStefan Richter 
1323d3816b8bSTakashi Sakamoto 	scoped_guard(spinlock_irq, &client->lock) {
1324e71d31daSStefan Richter 		generation = client->device->generation;
1325e71d31daSStefan Richter 		todo = r->todo;
1326d3816b8bSTakashi Sakamoto 		// Allow 1000ms grace period for other reallocations.
1327e71d31daSStefan Richter 		if (todo == ISO_RES_ALLOC &&
1328d3816b8bSTakashi Sakamoto 		    time_before64(get_jiffies_64(), client->device->card->reset_jiffies + HZ)) {
13299fb551bfSStefan Richter 			schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3));
1330e71d31daSStefan Richter 			skip = true;
1331e71d31daSStefan Richter 		} else {
1332d3816b8bSTakashi Sakamoto 			// We could be called twice within the same generation.
1333e71d31daSStefan Richter 			skip = todo == ISO_RES_REALLOC &&
1334e71d31daSStefan Richter 			       r->generation == generation;
1335e71d31daSStefan Richter 		}
1336e71d31daSStefan Richter 		free = todo == ISO_RES_DEALLOC ||
1337e71d31daSStefan Richter 		       todo == ISO_RES_ALLOC_ONCE ||
1338e71d31daSStefan Richter 		       todo == ISO_RES_DEALLOC_ONCE;
1339e71d31daSStefan Richter 		r->generation = generation;
1340d3816b8bSTakashi Sakamoto 	}
1341e71d31daSStefan Richter 
1342e71d31daSStefan Richter 	if (skip)
1343e71d31daSStefan Richter 		goto out;
1344e71d31daSStefan Richter 
1345e71d31daSStefan Richter 	bandwidth = r->bandwidth;
1346e71d31daSStefan Richter 
1347e71d31daSStefan Richter 	fw_iso_resource_manage(client->device->card, generation,
1348e71d31daSStefan Richter 			r->channels, &channel, &bandwidth,
1349e71d31daSStefan Richter 			todo == ISO_RES_ALLOC ||
1350e71d31daSStefan Richter 			todo == ISO_RES_REALLOC ||
1351f30e6d3eSStefan Richter 			todo == ISO_RES_ALLOC_ONCE);
1352e71d31daSStefan Richter 	/*
1353e71d31daSStefan Richter 	 * Is this generation outdated already?  As long as this resource sticks
1354d9f6c64eSTakashi Sakamoto 	 * in the xarray, it will be scheduled again for a newer generation or at
1355e71d31daSStefan Richter 	 * shutdown.
1356e71d31daSStefan Richter 	 */
1357e71d31daSStefan Richter 	if (channel == -EAGAIN &&
1358e71d31daSStefan Richter 	    (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC))
1359e71d31daSStefan Richter 		goto out;
1360e71d31daSStefan Richter 
1361e71d31daSStefan Richter 	success = channel >= 0 || bandwidth > 0;
1362e71d31daSStefan Richter 
1363d3816b8bSTakashi Sakamoto 	scoped_guard(spinlock_irq, &client->lock) {
1364d3816b8bSTakashi Sakamoto 		// Transit from allocation to reallocation, except if the client
1365d3816b8bSTakashi Sakamoto 		// requested deallocation in the meantime.
1366e71d31daSStefan Richter 		if (r->todo == ISO_RES_ALLOC)
1367e71d31daSStefan Richter 			r->todo = ISO_RES_REALLOC;
1368d3816b8bSTakashi Sakamoto 		// Allocation or reallocation failure?  Pull this resource out of the
1369d9f6c64eSTakashi Sakamoto 		// xarray and prepare for deletion, unless the client is shutting down.
1370e71d31daSStefan Richter 		if (r->todo == ISO_RES_REALLOC && !success &&
1371e71d31daSStefan Richter 		    !client->in_shutdown &&
1372d9f6c64eSTakashi Sakamoto 		    xa_erase(&client->resource_xa, index)) {
1373e71d31daSStefan Richter 			client_put(client);
1374e71d31daSStefan Richter 			free = true;
1375e71d31daSStefan Richter 		}
1376d3816b8bSTakashi Sakamoto 	}
1377e71d31daSStefan Richter 
1378e71d31daSStefan Richter 	if (todo == ISO_RES_ALLOC && channel >= 0)
1379e71d31daSStefan Richter 		r->channels = 1ULL << channel;
1380e71d31daSStefan Richter 
1381e71d31daSStefan Richter 	if (todo == ISO_RES_REALLOC && success)
1382e71d31daSStefan Richter 		goto out;
1383e71d31daSStefan Richter 
1384e71d31daSStefan Richter 	if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
1385e71d31daSStefan Richter 		e = r->e_alloc;
1386e71d31daSStefan Richter 		r->e_alloc = NULL;
1387e71d31daSStefan Richter 	} else {
1388e71d31daSStefan Richter 		e = r->e_dealloc;
1389e71d31daSStefan Richter 		r->e_dealloc = NULL;
1390e71d31daSStefan Richter 	}
1391e21fcf79SStefan Richter 	e->iso_resource.handle    = r->resource.handle;
1392e21fcf79SStefan Richter 	e->iso_resource.channel   = channel;
1393e21fcf79SStefan Richter 	e->iso_resource.bandwidth = bandwidth;
1394e71d31daSStefan Richter 
1395e71d31daSStefan Richter 	queue_event(client, &e->event,
1396e21fcf79SStefan Richter 		    &e->iso_resource, sizeof(e->iso_resource), NULL, 0);
1397e71d31daSStefan Richter 
1398e71d31daSStefan Richter 	if (free) {
1399e71d31daSStefan Richter 		cancel_delayed_work(&r->work);
1400e71d31daSStefan Richter 		kfree(r->e_alloc);
1401e71d31daSStefan Richter 		kfree(r->e_dealloc);
1402e71d31daSStefan Richter 		kfree(r);
1403e71d31daSStefan Richter 	}
1404e71d31daSStefan Richter  out:
1405e71d31daSStefan Richter 	client_put(client);
1406e71d31daSStefan Richter }
1407e71d31daSStefan Richter 
release_iso_resource(struct client * client,struct client_resource * resource)1408e71d31daSStefan Richter static void release_iso_resource(struct client *client,
1409e71d31daSStefan Richter 				 struct client_resource *resource)
1410e71d31daSStefan Richter {
1411ced2da31STakashi Sakamoto 	struct iso_resource *r = to_iso_resource(resource);
1412e71d31daSStefan Richter 
1413d3816b8bSTakashi Sakamoto 	guard(spinlock_irq)(&client->lock);
1414d3816b8bSTakashi Sakamoto 
1415e71d31daSStefan Richter 	r->todo = ISO_RES_DEALLOC;
14169fb551bfSStefan Richter 	schedule_iso_resource(r, 0);
1417e71d31daSStefan Richter }
1418e71d31daSStefan Richter 
init_iso_resource(struct client * client,struct fw_cdev_allocate_iso_resource * request,int todo)1419e71d31daSStefan Richter static int init_iso_resource(struct client *client,
1420e71d31daSStefan Richter 		struct fw_cdev_allocate_iso_resource *request, int todo)
1421e71d31daSStefan Richter {
1422e71d31daSStefan Richter 	struct iso_resource_event *e1, *e2;
1423e71d31daSStefan Richter 	struct iso_resource *r;
1424e71d31daSStefan Richter 	int ret;
1425e71d31daSStefan Richter 
1426e71d31daSStefan Richter 	if ((request->channels == 0 && request->bandwidth == 0) ||
1427bdabfa54SStefan Richter 	    request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL)
1428e71d31daSStefan Richter 		return -EINVAL;
1429e71d31daSStefan Richter 
1430e71d31daSStefan Richter 	r  = kmalloc(sizeof(*r), GFP_KERNEL);
1431e71d31daSStefan Richter 	e1 = kmalloc(sizeof(*e1), GFP_KERNEL);
1432e71d31daSStefan Richter 	e2 = kmalloc(sizeof(*e2), GFP_KERNEL);
1433e71d31daSStefan Richter 	if (r == NULL || e1 == NULL || e2 == NULL) {
1434e71d31daSStefan Richter 		ret = -ENOMEM;
1435e71d31daSStefan Richter 		goto fail;
1436e71d31daSStefan Richter 	}
1437e71d31daSStefan Richter 
1438e71d31daSStefan Richter 	INIT_DELAYED_WORK(&r->work, iso_resource_work);
1439e71d31daSStefan Richter 	r->client	= client;
1440e71d31daSStefan Richter 	r->todo		= todo;
1441e71d31daSStefan Richter 	r->generation	= -1;
1442e71d31daSStefan Richter 	r->channels	= request->channels;
1443e71d31daSStefan Richter 	r->bandwidth	= request->bandwidth;
1444e71d31daSStefan Richter 	r->e_alloc	= e1;
1445e71d31daSStefan Richter 	r->e_dealloc	= e2;
1446e71d31daSStefan Richter 
1447e21fcf79SStefan Richter 	e1->iso_resource.closure = request->closure;
1448e21fcf79SStefan Richter 	e1->iso_resource.type    = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED;
1449e21fcf79SStefan Richter 	e2->iso_resource.closure = request->closure;
1450e21fcf79SStefan Richter 	e2->iso_resource.type    = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
1451e71d31daSStefan Richter 
1452e71d31daSStefan Richter 	if (todo == ISO_RES_ALLOC) {
1453e71d31daSStefan Richter 		r->resource.release = release_iso_resource;
1454e71d31daSStefan Richter 		ret = add_client_resource(client, &r->resource, GFP_KERNEL);
1455e71d31daSStefan Richter 		if (ret < 0)
1456e71d31daSStefan Richter 			goto fail;
1457e71d31daSStefan Richter 	} else {
1458e71d31daSStefan Richter 		r->resource.release = NULL;
1459e71d31daSStefan Richter 		r->resource.handle = -1;
14609fb551bfSStefan Richter 		schedule_iso_resource(r, 0);
1461e71d31daSStefan Richter 	}
1462e71d31daSStefan Richter 	request->handle = r->resource.handle;
1463e71d31daSStefan Richter 
1464e71d31daSStefan Richter 	return 0;
1465e71d31daSStefan Richter  fail:
1466e71d31daSStefan Richter 	kfree(r);
1467e71d31daSStefan Richter 	kfree(e1);
1468e71d31daSStefan Richter 	kfree(e2);
1469e71d31daSStefan Richter 
1470e71d31daSStefan Richter 	return ret;
1471e71d31daSStefan Richter }
1472e71d31daSStefan Richter 
ioctl_allocate_iso_resource(struct client * client,union ioctl_arg * arg)14736e95dea7SStefan Richter static int ioctl_allocate_iso_resource(struct client *client,
14746e95dea7SStefan Richter 				       union ioctl_arg *arg)
1475e71d31daSStefan Richter {
14766e95dea7SStefan Richter 	return init_iso_resource(client,
14776e95dea7SStefan Richter 			&arg->allocate_iso_resource, ISO_RES_ALLOC);
1478e71d31daSStefan Richter }
1479e71d31daSStefan Richter 
ioctl_deallocate_iso_resource(struct client * client,union ioctl_arg * arg)14806e95dea7SStefan Richter static int ioctl_deallocate_iso_resource(struct client *client,
14816e95dea7SStefan Richter 					 union ioctl_arg *arg)
1482e71d31daSStefan Richter {
14836e95dea7SStefan Richter 	return release_client_resource(client,
14846e95dea7SStefan Richter 			arg->deallocate.handle, release_iso_resource, NULL);
1485e71d31daSStefan Richter }
1486e71d31daSStefan Richter 
ioctl_allocate_iso_resource_once(struct client * client,union ioctl_arg * arg)14876e95dea7SStefan Richter static int ioctl_allocate_iso_resource_once(struct client *client,
14886e95dea7SStefan Richter 					    union ioctl_arg *arg)
1489e71d31daSStefan Richter {
14906e95dea7SStefan Richter 	return init_iso_resource(client,
14916e95dea7SStefan Richter 			&arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE);
1492e71d31daSStefan Richter }
1493e71d31daSStefan Richter 
ioctl_deallocate_iso_resource_once(struct client * client,union ioctl_arg * arg)14946e95dea7SStefan Richter static int ioctl_deallocate_iso_resource_once(struct client *client,
14956e95dea7SStefan Richter 					      union ioctl_arg *arg)
1496e71d31daSStefan Richter {
14976e95dea7SStefan Richter 	return init_iso_resource(client,
14986e95dea7SStefan Richter 			&arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE);
1499e71d31daSStefan Richter }
1500e71d31daSStefan Richter 
1501e71d31daSStefan Richter /*
1502e71d31daSStefan Richter  * Returns a speed code:  Maximum speed to or from this device,
1503e71d31daSStefan Richter  * limited by the device's link speed, the local node's link speed,
1504e71d31daSStefan Richter  * and all PHY port speeds between the two links.
1505e71d31daSStefan Richter  */
ioctl_get_speed(struct client * client,union ioctl_arg * arg)15066e95dea7SStefan Richter static int ioctl_get_speed(struct client *client, union ioctl_arg *arg)
1507e71d31daSStefan Richter {
1508e71d31daSStefan Richter 	return client->device->max_speed;
1509e71d31daSStefan Richter }
1510e71d31daSStefan Richter 
ioctl_send_broadcast_request(struct client * client,union ioctl_arg * arg)15116e95dea7SStefan Richter static int ioctl_send_broadcast_request(struct client *client,
15126e95dea7SStefan Richter 					union ioctl_arg *arg)
1513e71d31daSStefan Richter {
15146e95dea7SStefan Richter 	struct fw_cdev_send_request *a = &arg->send_request;
1515e71d31daSStefan Richter 
15166e95dea7SStefan Richter 	switch (a->tcode) {
1517e71d31daSStefan Richter 	case TCODE_WRITE_QUADLET_REQUEST:
1518e71d31daSStefan Richter 	case TCODE_WRITE_BLOCK_REQUEST:
1519e71d31daSStefan Richter 		break;
1520e71d31daSStefan Richter 	default:
1521e71d31daSStefan Richter 		return -EINVAL;
1522e71d31daSStefan Richter 	}
1523e71d31daSStefan Richter 
1524e71d31daSStefan Richter 	/* Security policy: Only allow accesses to Units Space. */
15256e95dea7SStefan Richter 	if (a->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END)
1526e71d31daSStefan Richter 		return -EACCES;
1527e71d31daSStefan Richter 
15286e95dea7SStefan Richter 	return init_request(client, a, LOCAL_BUS | 0x3f, SCODE_100);
1529e71d31daSStefan Richter }
1530e71d31daSStefan Richter 
ioctl_send_stream_packet(struct client * client,union ioctl_arg * arg)15316e95dea7SStefan Richter static int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg)
1532e71d31daSStefan Richter {
15336e95dea7SStefan Richter 	struct fw_cdev_send_stream_packet *a = &arg->send_stream_packet;
1534e71d31daSStefan Richter 	struct fw_cdev_send_request request;
1535e71d31daSStefan Richter 	int dest;
1536e71d31daSStefan Richter 
15376e95dea7SStefan Richter 	if (a->speed > client->device->card->link_speed ||
15386e95dea7SStefan Richter 	    a->length > 1024 << a->speed)
1539e71d31daSStefan Richter 		return -EIO;
1540e71d31daSStefan Richter 
15416e95dea7SStefan Richter 	if (a->tag > 3 || a->channel > 63 || a->sy > 15)
1542e71d31daSStefan Richter 		return -EINVAL;
1543e71d31daSStefan Richter 
15446e95dea7SStefan Richter 	dest = fw_stream_packet_destination_id(a->tag, a->channel, a->sy);
1545e71d31daSStefan Richter 	request.tcode		= TCODE_STREAM_DATA;
15466e95dea7SStefan Richter 	request.length		= a->length;
15476e95dea7SStefan Richter 	request.closure		= a->closure;
15486e95dea7SStefan Richter 	request.data		= a->data;
15496e95dea7SStefan Richter 	request.generation	= a->generation;
1550e71d31daSStefan Richter 
15516e95dea7SStefan Richter 	return init_request(client, &request, dest, a->speed);
1552e71d31daSStefan Richter }
1553e71d31daSStefan Richter 
outbound_phy_packet_callback(struct fw_packet * packet,struct fw_card * card,int status)1554850bb6f2SStefan Richter static void outbound_phy_packet_callback(struct fw_packet *packet,
1555850bb6f2SStefan Richter 					 struct fw_card *card, int status)
1556850bb6f2SStefan Richter {
1557850bb6f2SStefan Richter 	struct outbound_phy_packet_event *e =
1558850bb6f2SStefan Richter 		container_of(packet, struct outbound_phy_packet_event, p);
15591ef14771STakashi Sakamoto 	struct client *e_client = e->client;
15601ef14771STakashi Sakamoto 	u32 rcode;
1561850bb6f2SStefan Richter 
1562810f2aa8STakashi Sakamoto 	trace_async_phy_outbound_complete((uintptr_t)packet, card->index, status, packet->generation,
15631a4c53cfSTakashi Sakamoto 					  packet->timestamp);
15641a4c53cfSTakashi Sakamoto 
1565850bb6f2SStefan Richter 	switch (status) {
15661ef14771STakashi Sakamoto 	// expected:
15671ef14771STakashi Sakamoto 	case ACK_COMPLETE:
15681ef14771STakashi Sakamoto 		rcode = RCODE_COMPLETE;
15691ef14771STakashi Sakamoto 		break;
15701ef14771STakashi Sakamoto 	// should never happen with PHY packets:
15711ef14771STakashi Sakamoto 	case ACK_PENDING:
15721ef14771STakashi Sakamoto 		rcode = RCODE_COMPLETE;
15731ef14771STakashi Sakamoto 		break;
1574850bb6f2SStefan Richter 	case ACK_BUSY_X:
1575850bb6f2SStefan Richter 	case ACK_BUSY_A:
15761ef14771STakashi Sakamoto 	case ACK_BUSY_B:
15771ef14771STakashi Sakamoto 		rcode = RCODE_BUSY;
15781ef14771STakashi Sakamoto 		break;
15791ef14771STakashi Sakamoto 	case ACK_DATA_ERROR:
15801ef14771STakashi Sakamoto 		rcode = RCODE_DATA_ERROR;
15811ef14771STakashi Sakamoto 		break;
15821ef14771STakashi Sakamoto 	case ACK_TYPE_ERROR:
15831ef14771STakashi Sakamoto 		rcode = RCODE_TYPE_ERROR;
15841ef14771STakashi Sakamoto 		break;
15851ef14771STakashi Sakamoto 	// stale generation; cancelled; on certain controllers: no ack
15861ef14771STakashi Sakamoto 	default:
15871ef14771STakashi Sakamoto 		rcode = status;
15881ef14771STakashi Sakamoto 		break;
1589850bb6f2SStefan Richter 	}
1590850bb6f2SStefan Richter 
1591fe971f91STakashi Sakamoto 	switch (e->phy_packet.without_tstamp.type) {
1592fe971f91STakashi Sakamoto 	case FW_CDEV_EVENT_PHY_PACKET_SENT:
1593fe971f91STakashi Sakamoto 	{
1594fe971f91STakashi Sakamoto 		struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp;
1595fe971f91STakashi Sakamoto 
15961ef14771STakashi Sakamoto 		pp->rcode = rcode;
15971ef14771STakashi Sakamoto 		pp->data[0] = packet->timestamp;
1598fe971f91STakashi Sakamoto 		queue_event(e->client, &e->event, &e->phy_packet, sizeof(*pp) + pp->length,
1599fe971f91STakashi Sakamoto 			    NULL, 0);
1600fe971f91STakashi Sakamoto 		break;
1601fe971f91STakashi Sakamoto 	}
1602fe971f91STakashi Sakamoto 	case FW_CDEV_EVENT_PHY_PACKET_SENT2:
1603fe971f91STakashi Sakamoto 	{
1604fe971f91STakashi Sakamoto 		struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp;
1605fe971f91STakashi Sakamoto 
1606fe971f91STakashi Sakamoto 		pp->rcode = rcode;
1607fe971f91STakashi Sakamoto 		pp->tstamp = packet->timestamp;
1608fe971f91STakashi Sakamoto 		queue_event(e->client, &e->event, &e->phy_packet, sizeof(*pp) + pp->length,
1609fe971f91STakashi Sakamoto 			    NULL, 0);
1610fe971f91STakashi Sakamoto 		break;
1611fe971f91STakashi Sakamoto 	}
1612fe971f91STakashi Sakamoto 	default:
1613fe971f91STakashi Sakamoto 		WARN_ON(1);
1614fe971f91STakashi Sakamoto 		break;
1615fe971f91STakashi Sakamoto 	}
16161ef14771STakashi Sakamoto 
1617b7c81f80SChengfeng Ye 	client_put(e_client);
1618850bb6f2SStefan Richter }
1619850bb6f2SStefan Richter 
ioctl_send_phy_packet(struct client * client,union ioctl_arg * arg)1620850bb6f2SStefan Richter static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)
1621850bb6f2SStefan Richter {
1622850bb6f2SStefan Richter 	struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet;
1623850bb6f2SStefan Richter 	struct fw_card *card = client->device->card;
1624850bb6f2SStefan Richter 	struct outbound_phy_packet_event *e;
1625850bb6f2SStefan Richter 
1626850bb6f2SStefan Richter 	/* Access policy: Allow this ioctl only on local nodes' device files. */
1627850bb6f2SStefan Richter 	if (!client->device->is_local)
1628850bb6f2SStefan Richter 		return -ENOSYS;
1629850bb6f2SStefan Richter 
1630fe971f91STakashi Sakamoto 	e = kzalloc(sizeof(*e) + sizeof(a->data), GFP_KERNEL);
1631850bb6f2SStefan Richter 	if (e == NULL)
1632850bb6f2SStefan Richter 		return -ENOMEM;
1633850bb6f2SStefan Richter 
1634850bb6f2SStefan Richter 	client_get(client);
1635850bb6f2SStefan Richter 	e->client		= client;
1636850bb6f2SStefan Richter 	e->p.speed		= SCODE_100;
1637850bb6f2SStefan Richter 	e->p.generation		= a->generation;
16389b6ad6a0STakashi Sakamoto 	async_header_set_tcode(e->p.header, TCODE_LINK_INTERNAL);
16395b06db16SClemens Ladisch 	e->p.header[1]		= a->data[0];
16405b06db16SClemens Ladisch 	e->p.header[2]		= a->data[1];
16415b06db16SClemens Ladisch 	e->p.header_length	= 12;
1642850bb6f2SStefan Richter 	e->p.callback		= outbound_phy_packet_callback;
16431ef14771STakashi Sakamoto 
1644fe971f91STakashi Sakamoto 	if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) {
1645fe971f91STakashi Sakamoto 		struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp;
1646fe971f91STakashi Sakamoto 
16471ef14771STakashi Sakamoto 		pp->closure = a->closure;
16481ef14771STakashi Sakamoto 		pp->type = FW_CDEV_EVENT_PHY_PACKET_SENT;
1649cc550216SStefan Richter 		if (is_ping_packet(a->data))
16501ef14771STakashi Sakamoto 			pp->length = 4;
1651fe971f91STakashi Sakamoto 	} else {
1652fe971f91STakashi Sakamoto 		struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp;
1653fe971f91STakashi Sakamoto 
1654fe971f91STakashi Sakamoto 		pp->closure = a->closure;
1655fe971f91STakashi Sakamoto 		pp->type = FW_CDEV_EVENT_PHY_PACKET_SENT2;
1656fe971f91STakashi Sakamoto 		// Keep the data field so that application can match the response event to the
1657fe971f91STakashi Sakamoto 		// request.
1658fe971f91STakashi Sakamoto 		pp->length = sizeof(a->data);
1659fe971f91STakashi Sakamoto 		memcpy(pp->data, a->data, sizeof(a->data));
1660fe971f91STakashi Sakamoto 	}
1661850bb6f2SStefan Richter 
16623cb44a72STakashi Sakamoto 	trace_async_phy_outbound_initiate((uintptr_t)&e->p, card->index, e->p.generation,
16633cb44a72STakashi Sakamoto 					  e->p.header[1], e->p.header[2]);
16641a4c53cfSTakashi Sakamoto 
1665850bb6f2SStefan Richter 	card->driver->send_request(card, &e->p);
1666850bb6f2SStefan Richter 
1667850bb6f2SStefan Richter 	return 0;
1668850bb6f2SStefan Richter }
1669850bb6f2SStefan Richter 
ioctl_receive_phy_packets(struct client * client,union ioctl_arg * arg)1670bf54e146SStefan Richter static int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg)
1671bf54e146SStefan Richter {
1672bf54e146SStefan Richter 	struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets;
1673bf54e146SStefan Richter 	struct fw_card *card = client->device->card;
1674bf54e146SStefan Richter 
1675bf54e146SStefan Richter 	/* Access policy: Allow this ioctl only on local nodes' device files. */
1676bf54e146SStefan Richter 	if (!client->device->is_local)
1677bf54e146SStefan Richter 		return -ENOSYS;
1678bf54e146SStefan Richter 
1679b9545448STakashi Sakamoto 	guard(spinlock_irq)(&card->lock);
1680bf54e146SStefan Richter 
1681bf54e146SStefan Richter 	list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list);
1682bf54e146SStefan Richter 	client->phy_receiver_closure = a->closure;
1683bf54e146SStefan Richter 
1684bf54e146SStefan Richter 	return 0;
1685bf54e146SStefan Richter }
1686bf54e146SStefan Richter 
fw_cdev_handle_phy_packet(struct fw_card * card,struct fw_packet * p)1687bf54e146SStefan Richter void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p)
1688bf54e146SStefan Richter {
1689bf54e146SStefan Richter 	struct client *client;
1690bf54e146SStefan Richter 
1691b9545448STakashi Sakamoto 	guard(spinlock_irqsave)(&card->lock);
1692bf54e146SStefan Richter 
1693bf54e146SStefan Richter 	list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) {
1694b9545448STakashi Sakamoto 		struct inbound_phy_packet_event *e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC);
1695cfb0c9d1SStefan Richter 		if (e == NULL)
1696bf54e146SStefan Richter 			break;
1697cfb0c9d1SStefan Richter 
1698fe971f91STakashi Sakamoto 		if (client->version < FW_CDEV_VERSION_EVENT_ASYNC_TSTAMP) {
1699fe971f91STakashi Sakamoto 			struct fw_cdev_event_phy_packet *pp = &e->phy_packet.without_tstamp;
1700fe971f91STakashi Sakamoto 
17011ef14771STakashi Sakamoto 			pp->closure = client->phy_receiver_closure;
17021ef14771STakashi Sakamoto 			pp->type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED;
17031ef14771STakashi Sakamoto 			pp->rcode = RCODE_COMPLETE;
17041ef14771STakashi Sakamoto 			pp->length = 8;
17051ef14771STakashi Sakamoto 			pp->data[0] = p->header[1];
17061ef14771STakashi Sakamoto 			pp->data[1] = p->header[2];
17071ef14771STakashi Sakamoto 			queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0);
1708fe971f91STakashi Sakamoto 		} else {
1709fe971f91STakashi Sakamoto 			struct fw_cdev_event_phy_packet2 *pp = &e->phy_packet.with_tstamp;
1710fe971f91STakashi Sakamoto 
1711fe971f91STakashi Sakamoto 			pp = &e->phy_packet.with_tstamp;
1712fe971f91STakashi Sakamoto 			pp->closure = client->phy_receiver_closure;
1713fe971f91STakashi Sakamoto 			pp->type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED2;
1714fe971f91STakashi Sakamoto 			pp->rcode = RCODE_COMPLETE;
1715fe971f91STakashi Sakamoto 			pp->length = 8;
1716fe971f91STakashi Sakamoto 			pp->tstamp = p->timestamp;
1717fe971f91STakashi Sakamoto 			pp->data[0] = p->header[1];
1718fe971f91STakashi Sakamoto 			pp->data[1] = p->header[2];
1719fe971f91STakashi Sakamoto 			queue_event(client, &e->event, &e->phy_packet, sizeof(*pp) + 8, NULL, 0);
1720fe971f91STakashi Sakamoto 		}
1721bf54e146SStefan Richter 	}
1722bf54e146SStefan Richter }
1723bf54e146SStefan Richter 
17246e95dea7SStefan Richter static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
1725b9dc61cfSStefan Richter 	[0x00] = ioctl_get_info,
1726b9dc61cfSStefan Richter 	[0x01] = ioctl_send_request,
1727b9dc61cfSStefan Richter 	[0x02] = ioctl_allocate,
1728b9dc61cfSStefan Richter 	[0x03] = ioctl_deallocate,
1729b9dc61cfSStefan Richter 	[0x04] = ioctl_send_response,
1730b9dc61cfSStefan Richter 	[0x05] = ioctl_initiate_bus_reset,
1731b9dc61cfSStefan Richter 	[0x06] = ioctl_add_descriptor,
1732b9dc61cfSStefan Richter 	[0x07] = ioctl_remove_descriptor,
1733b9dc61cfSStefan Richter 	[0x08] = ioctl_create_iso_context,
1734b9dc61cfSStefan Richter 	[0x09] = ioctl_queue_iso,
1735b9dc61cfSStefan Richter 	[0x0a] = ioctl_start_iso,
1736b9dc61cfSStefan Richter 	[0x0b] = ioctl_stop_iso,
1737b9dc61cfSStefan Richter 	[0x0c] = ioctl_get_cycle_timer,
1738b9dc61cfSStefan Richter 	[0x0d] = ioctl_allocate_iso_resource,
1739b9dc61cfSStefan Richter 	[0x0e] = ioctl_deallocate_iso_resource,
1740b9dc61cfSStefan Richter 	[0x0f] = ioctl_allocate_iso_resource_once,
1741b9dc61cfSStefan Richter 	[0x10] = ioctl_deallocate_iso_resource_once,
1742b9dc61cfSStefan Richter 	[0x11] = ioctl_get_speed,
1743b9dc61cfSStefan Richter 	[0x12] = ioctl_send_broadcast_request,
1744b9dc61cfSStefan Richter 	[0x13] = ioctl_send_stream_packet,
1745b9dc61cfSStefan Richter 	[0x14] = ioctl_get_cycle_timer2,
1746850bb6f2SStefan Richter 	[0x15] = ioctl_send_phy_packet,
1747bf54e146SStefan Richter 	[0x16] = ioctl_receive_phy_packets,
1748872e330eSStefan Richter 	[0x17] = ioctl_set_iso_channels,
1749d1bbd209SClemens Ladisch 	[0x18] = ioctl_flush_iso,
1750e71d31daSStefan Richter };
1751e71d31daSStefan Richter 
dispatch_ioctl(struct client * client,unsigned int cmd,void __user * arg)1752e71d31daSStefan Richter static int dispatch_ioctl(struct client *client,
1753e71d31daSStefan Richter 			  unsigned int cmd, void __user *arg)
1754e71d31daSStefan Richter {
17556e95dea7SStefan Richter 	union ioctl_arg buffer;
1756e71d31daSStefan Richter 	int ret;
1757e71d31daSStefan Richter 
175864582298SStefan Richter 	if (fw_device_is_shutdown(client->device))
175964582298SStefan Richter 		return -ENODEV;
176064582298SStefan Richter 
1761e71d31daSStefan Richter 	if (_IOC_TYPE(cmd) != '#' ||
17629cac00b8SStefan Richter 	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) ||
17639cac00b8SStefan Richter 	    _IOC_SIZE(cmd) > sizeof(buffer))
1764d873d794SStefan Richter 		return -ENOTTY;
1765e71d31daSStefan Richter 
1766eaca2d8eSStefan Richter 	memset(&buffer, 0, sizeof(buffer));
17679cac00b8SStefan Richter 
17689cac00b8SStefan Richter 	if (_IOC_DIR(cmd) & _IOC_WRITE)
17699cac00b8SStefan Richter 		if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd)))
1770e71d31daSStefan Richter 			return -EFAULT;
1771e71d31daSStefan Richter 
17726e95dea7SStefan Richter 	ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer);
1773e71d31daSStefan Richter 	if (ret < 0)
1774e71d31daSStefan Richter 		return ret;
1775e71d31daSStefan Richter 
17769cac00b8SStefan Richter 	if (_IOC_DIR(cmd) & _IOC_READ)
17779cac00b8SStefan Richter 		if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd)))
1778e71d31daSStefan Richter 			return -EFAULT;
1779e71d31daSStefan Richter 
1780e71d31daSStefan Richter 	return ret;
1781e71d31daSStefan Richter }
1782e71d31daSStefan Richter 
fw_device_op_ioctl(struct file * file,unsigned int cmd,unsigned long arg)1783e71d31daSStefan Richter static long fw_device_op_ioctl(struct file *file,
1784e71d31daSStefan Richter 			       unsigned int cmd, unsigned long arg)
1785e71d31daSStefan Richter {
178664582298SStefan Richter 	return dispatch_ioctl(file->private_data, cmd, (void __user *)arg);
1787e71d31daSStefan Richter }
1788e71d31daSStefan Richter 
fw_device_op_mmap(struct file * file,struct vm_area_struct * vma)1789e71d31daSStefan Richter static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
1790e71d31daSStefan Richter {
1791e71d31daSStefan Richter 	struct client *client = file->private_data;
1792e71d31daSStefan Richter 	unsigned long size;
1793e71d31daSStefan Richter 	int page_count, ret;
1794e71d31daSStefan Richter 
1795e71d31daSStefan Richter 	if (fw_device_is_shutdown(client->device))
1796e71d31daSStefan Richter 		return -ENODEV;
1797e71d31daSStefan Richter 
1798e71d31daSStefan Richter 	/* FIXME: We could support multiple buffers, but we don't. */
1799e71d31daSStefan Richter 	if (client->buffer.pages != NULL)
1800e71d31daSStefan Richter 		return -EBUSY;
1801e71d31daSStefan Richter 
1802e71d31daSStefan Richter 	if (!(vma->vm_flags & VM_SHARED))
1803e71d31daSStefan Richter 		return -EINVAL;
1804e71d31daSStefan Richter 
1805e71d31daSStefan Richter 	if (vma->vm_start & ~PAGE_MASK)
1806e71d31daSStefan Richter 		return -EINVAL;
1807e71d31daSStefan Richter 
1808e71d31daSStefan Richter 	client->vm_start = vma->vm_start;
1809e71d31daSStefan Richter 	size = vma->vm_end - vma->vm_start;
1810e71d31daSStefan Richter 	page_count = size >> PAGE_SHIFT;
1811e71d31daSStefan Richter 	if (size & ~PAGE_MASK)
1812e71d31daSStefan Richter 		return -EINVAL;
1813e71d31daSStefan Richter 
18140b6c4857SStefan Richter 	ret = fw_iso_buffer_alloc(&client->buffer, page_count);
1815e71d31daSStefan Richter 	if (ret < 0)
1816e71d31daSStefan Richter 		return ret;
1817e71d31daSStefan Richter 
1818cf123b01STakashi Sakamoto 	scoped_guard(spinlock_irq, &client->lock) {
18190b6c4857SStefan Richter 		if (client->iso_context) {
1820cf123b01STakashi Sakamoto 			ret = fw_iso_buffer_map_dma(&client->buffer, client->device->card,
18210b6c4857SStefan Richter 						    iso_dma_direction(client->iso_context));
1822e71d31daSStefan Richter 			if (ret < 0)
18230b6c4857SStefan Richter 				goto fail;
1824cf123b01STakashi Sakamoto 			client->buffer_is_mapped = true;
1825cf123b01STakashi Sakamoto 		}
1826cf123b01STakashi Sakamoto 	}
1827e71d31daSStefan Richter 
18287807759eSStefan Richter 	ret = vm_map_pages_zero(vma, client->buffer.pages,
18297807759eSStefan Richter 				client->buffer.page_count);
18300b6c4857SStefan Richter 	if (ret < 0)
18310b6c4857SStefan Richter 		goto fail;
18320b6c4857SStefan Richter 
18330b6c4857SStefan Richter 	return 0;
18340b6c4857SStefan Richter  fail:
18350b6c4857SStefan Richter 	fw_iso_buffer_destroy(&client->buffer, client->device->card);
1836e71d31daSStefan Richter 	return ret;
1837e71d31daSStefan Richter }
1838e71d31daSStefan Richter 
has_outbound_transactions(struct client * client)18396ec9e926STakashi Sakamoto static bool has_outbound_transactions(struct client *client)
18405a5e62daSClemens Ladisch {
18416ec9e926STakashi Sakamoto 	struct client_resource *resource;
1842d9f6c64eSTakashi Sakamoto 	unsigned long index;
18435a5e62daSClemens Ladisch 
1844d3816b8bSTakashi Sakamoto 	guard(spinlock_irq)(&client->lock);
18455a5e62daSClemens Ladisch 
1846d9f6c64eSTakashi Sakamoto 	xa_for_each(&client->resource_xa, index, resource) {
18476ec9e926STakashi Sakamoto 		if (is_outbound_transaction_resource(resource))
18486ec9e926STakashi Sakamoto 			return true;
18495a5e62daSClemens Ladisch 	}
18505a5e62daSClemens Ladisch 
18516ec9e926STakashi Sakamoto 	return false;
1852e71d31daSStefan Richter }
1853e71d31daSStefan Richter 
fw_device_op_release(struct inode * inode,struct file * file)1854e71d31daSStefan Richter static int fw_device_op_release(struct inode *inode, struct file *file)
1855e71d31daSStefan Richter {
1856e71d31daSStefan Richter 	struct client *client = file->private_data;
1857e21fcf79SStefan Richter 	struct event *event, *next_event;
18586ec9e926STakashi Sakamoto 	struct client_resource *resource;
1859d9f6c64eSTakashi Sakamoto 	unsigned long index;
1860e71d31daSStefan Richter 
1861b9545448STakashi Sakamoto 	scoped_guard(spinlock_irq, &client->device->card->lock)
1862bf54e146SStefan Richter 		list_del(&client->phy_receiver_link);
1863bf54e146SStefan Richter 
1864044ce581STakashi Sakamoto 	scoped_guard(mutex, &client->device->client_list_mutex)
1865e71d31daSStefan Richter 		list_del(&client->link);
1866e71d31daSStefan Richter 
1867e71d31daSStefan Richter 	if (client->iso_context)
1868e71d31daSStefan Richter 		fw_iso_context_destroy(client->iso_context);
1869e71d31daSStefan Richter 
1870e71d31daSStefan Richter 	if (client->buffer.pages)
1871e71d31daSStefan Richter 		fw_iso_buffer_destroy(&client->buffer, client->device->card);
1872e71d31daSStefan Richter 
1873d9f6c64eSTakashi Sakamoto 	// Freeze client->resource_xa and client->event_list.
18744f1f91aeSTakashi Sakamoto 	scoped_guard(spinlock_irq, &client->lock)
1875e71d31daSStefan Richter 		client->in_shutdown = true;
1876e71d31daSStefan Richter 
18775a5e62daSClemens Ladisch 	wait_event(client->tx_flush_wait, !has_outbound_transactions(client));
18785a5e62daSClemens Ladisch 
1879d9f6c64eSTakashi Sakamoto 	xa_for_each(&client->resource_xa, index, resource) {
18806ec9e926STakashi Sakamoto 		resource->release(client, resource);
18816ec9e926STakashi Sakamoto 		client_put(client);
18826ec9e926STakashi Sakamoto 	}
1883d9f6c64eSTakashi Sakamoto 	xa_destroy(&client->resource_xa);
1884e71d31daSStefan Richter 
1885e21fcf79SStefan Richter 	list_for_each_entry_safe(event, next_event, &client->event_list, link)
1886e21fcf79SStefan Richter 		kfree(event);
1887e71d31daSStefan Richter 
1888e71d31daSStefan Richter 	client_put(client);
1889e71d31daSStefan Richter 
1890e71d31daSStefan Richter 	return 0;
1891e71d31daSStefan Richter }
1892e71d31daSStefan Richter 
fw_device_op_poll(struct file * file,poll_table * pt)1893afc9a42bSAl Viro static __poll_t fw_device_op_poll(struct file *file, poll_table * pt)
1894e71d31daSStefan Richter {
1895e71d31daSStefan Richter 	struct client *client = file->private_data;
1896afc9a42bSAl Viro 	__poll_t mask = 0;
1897e71d31daSStefan Richter 
1898e71d31daSStefan Richter 	poll_wait(file, &client->wait, pt);
1899e71d31daSStefan Richter 
1900e71d31daSStefan Richter 	if (fw_device_is_shutdown(client->device))
1901a9a08845SLinus Torvalds 		mask |= EPOLLHUP | EPOLLERR;
1902e71d31daSStefan Richter 	if (!list_empty(&client->event_list))
1903a9a08845SLinus Torvalds 		mask |= EPOLLIN | EPOLLRDNORM;
1904e71d31daSStefan Richter 
1905e71d31daSStefan Richter 	return mask;
1906e71d31daSStefan Richter }
1907e71d31daSStefan Richter 
1908e71d31daSStefan Richter const struct file_operations fw_device_ops = {
1909e71d31daSStefan Richter 	.owner		= THIS_MODULE,
1910e71d31daSStefan Richter 	.open		= fw_device_op_open,
1911e71d31daSStefan Richter 	.read		= fw_device_op_read,
1912e71d31daSStefan Richter 	.unlocked_ioctl	= fw_device_op_ioctl,
1913e71d31daSStefan Richter 	.mmap		= fw_device_op_mmap,
19143ac26b2eSStefan Richter 	.release	= fw_device_op_release,
19153ac26b2eSStefan Richter 	.poll		= fw_device_op_poll,
1916407e9ef7SArnd Bergmann 	.compat_ioctl	= compat_ptr_ioctl,
1917e71d31daSStefan Richter };
1918