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, ®ion);
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