Blame view

drivers/firewire/core-cdev.c 46.1 KB
c781c06d1   Kristian Høgsberg   firewire: Clean u...
1
2
  /*
   * Char device for device raw access
19a15b937   Kristian Høgsberg   firewire: Add dev...
3
   *
c781c06d1   Kristian Høgsberg   firewire: Clean u...
4
   * Copyright (C) 2005-2007  Kristian Hoegsberg <krh@bitplanet.net>
19a15b937   Kristian Høgsberg   firewire: Add dev...
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software Foundation,
   * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   */
eb5b35a56   Stefan Richter   firewire: core: e...
20
  #include <linux/bug.h>
be5bbd675   Stefan Richter   firewire: cdev: s...
21
22
23
  #include <linux/compat.h>
  #include <linux/delay.h>
  #include <linux/device.h>
0b6c4857f   Stefan Richter   firewire: core: f...
24
  #include <linux/dma-mapping.h>
be5bbd675   Stefan Richter   firewire: cdev: s...
25
  #include <linux/errno.h>
77c9a5daa   Stefan Richter   firewire: reorgan...
26
  #include <linux/firewire.h>
be5bbd675   Stefan Richter   firewire: cdev: s...
27
28
  #include <linux/firewire-cdev.h>
  #include <linux/idr.h>
4a9bde9b8   Stefan Richter   firewire: get_cyc...
29
  #include <linux/irqflags.h>
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
30
  #include <linux/jiffies.h>
19a15b937   Kristian Høgsberg   firewire: Add dev...
31
  #include <linux/kernel.h>
fb4430367   Stefan Richter   firewire: cdev: r...
32
  #include <linux/kref.h>
be5bbd675   Stefan Richter   firewire: cdev: s...
33
34
  #include <linux/mm.h>
  #include <linux/module.h>
d67cfb961   Stefan Richter   firewire: convert...
35
  #include <linux/mutex.h>
19a15b937   Kristian Høgsberg   firewire: Add dev...
36
  #include <linux/poll.h>
ae2a97661   Stefan Richter   firewire: core: s...
37
  #include <linux/sched.h> /* required for linux/wait.h */
5a0e3ad6a   Tejun Heo   include cleanup: ...
38
  #include <linux/slab.h>
cf417e549   Jay Fenlason   firewire: add a c...
39
  #include <linux/spinlock.h>
281e20323   Stefan Richter   firewire: core: f...
40
  #include <linux/string.h>
be5bbd675   Stefan Richter   firewire: cdev: s...
41
  #include <linux/time.h>
e034d2425   Stefan Richter   firewire: core: i...
42
  #include <linux/uaccess.h>
be5bbd675   Stefan Richter   firewire: cdev: s...
43
44
  #include <linux/vmalloc.h>
  #include <linux/wait.h>
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
45
  #include <linux/workqueue.h>
be5bbd675   Stefan Richter   firewire: cdev: s...
46

be5bbd675   Stefan Richter   firewire: cdev: s...
47

77c9a5daa   Stefan Richter   firewire: reorgan...
48
  #include "core.h"
19a15b937   Kristian Høgsberg   firewire: Add dev...
49

604f45167   Stefan Richter   firewire: cdev: f...
50
51
52
  /*
   * ABI version history is documented in linux/firewire-cdev.h.
   */
18d627113   Clemens Ladisch   firewire: prevent...
53
  #define FW_CDEV_KERNEL_VERSION			5
8e2b2b46e   Stefan Richter   firewire: cdev: i...
54
55
  #define FW_CDEV_VERSION_EVENT_REQUEST2		4
  #define FW_CDEV_VERSION_ALLOCATE_REGION_END	4
604f45167   Stefan Richter   firewire: cdev: f...
56

19a15b937   Kristian Høgsberg   firewire: Add dev...
57
  struct client {
344bbc4de   Kristian Høgsberg   firewire: General...
58
  	u32 version;
19a15b937   Kristian Høgsberg   firewire: Add dev...
59
  	struct fw_device *device;
45ee3199e   Jay Fenlason   firewire: cdev: u...
60

19a15b937   Kristian Høgsberg   firewire: Add dev...
61
  	spinlock_t lock;
45ee3199e   Jay Fenlason   firewire: cdev: u...
62
63
  	bool in_shutdown;
  	struct idr resource_idr;
19a15b937   Kristian Høgsberg   firewire: Add dev...
64
  	struct list_head event_list;
19a15b937   Kristian Høgsberg   firewire: Add dev...
65
  	wait_queue_head_t wait;
5a5e62da9   Clemens Ladisch   firewire: cdev: a...
66
  	wait_queue_head_t tx_flush_wait;
da8ecffae   Kristian Høgsberg   firewire: Streaml...
67
  	u64 bus_reset_closure;
9aad81253   Kristian Høgsberg   firewire: Split t...
68

19a15b937   Kristian Høgsberg   firewire: Add dev...
69
  	struct fw_iso_context *iso_context;
abaa5743e   Kristian Høgsberg   firewire: Future ...
70
  	u64 iso_closure;
9aad81253   Kristian Høgsberg   firewire: Split t...
71
72
  	struct fw_iso_buffer buffer;
  	unsigned long vm_start;
0b6c4857f   Stefan Richter   firewire: core: f...
73
  	bool buffer_is_mapped;
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
74

bf54e1462   Stefan Richter   firewire: cdev: a...
75
76
  	struct list_head phy_receiver_link;
  	u64 phy_receiver_closure;
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
77
  	struct list_head link;
fb4430367   Stefan Richter   firewire: cdev: r...
78
  	struct kref kref;
19a15b937   Kristian Høgsberg   firewire: Add dev...
79
  };
fb4430367   Stefan Richter   firewire: cdev: r...
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  static inline void client_get(struct client *client)
  {
  	kref_get(&client->kref);
  }
  
  static void client_release(struct kref *kref)
  {
  	struct client *client = container_of(kref, struct client, kref);
  
  	fw_device_put(client->device);
  	kfree(client);
  }
  
  static void client_put(struct client *client)
  {
  	kref_put(&client->kref, client_release);
  }
97c18b7fd   Stefan Richter   firewire: cdev: u...
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
  struct client_resource;
  typedef void (*client_resource_release_fn_t)(struct client *,
  					     struct client_resource *);
  struct client_resource {
  	client_resource_release_fn_t release;
  	int handle;
  };
  
  struct address_handler_resource {
  	struct client_resource resource;
  	struct fw_address_handler handler;
  	__u64 closure;
  	struct client *client;
  };
  
  struct outbound_transaction_resource {
  	struct client_resource resource;
  	struct fw_transaction transaction;
  };
  
  struct inbound_transaction_resource {
  	struct client_resource resource;
08bd34c98   Jay Fenlason   firewire: cdev: f...
119
  	struct fw_card *card;
97c18b7fd   Stefan Richter   firewire: cdev: u...
120
121
122
123
124
125
126
127
128
129
  	struct fw_request *request;
  	void *data;
  	size_t length;
  };
  
  struct descriptor_resource {
  	struct client_resource resource;
  	struct fw_descriptor descriptor;
  	u32 data[0];
  };
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
130
131
132
133
134
  struct iso_resource {
  	struct client_resource resource;
  	struct client *client;
  	/* Schedule work and access todo only with client->lock held. */
  	struct delayed_work work;
1ec3c0269   Stefan Richter   firewire: cdev: a...
135
136
  	enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
  	      ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
137
138
139
140
141
  	int generation;
  	u64 channels;
  	s32 bandwidth;
  	struct iso_resource_event *e_alloc, *e_dealloc;
  };
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
142
  static void release_iso_resource(struct client *, struct client_resource *);
9fb551bf7   Stefan Richter   firewire: normali...
143
144
145
  static void schedule_iso_resource(struct iso_resource *r, unsigned long delay)
  {
  	client_get(r->client);
105e53f86   Stefan Richter   firewire: sbp2: p...
146
  	if (!queue_delayed_work(fw_workqueue, &r->work, delay))
9fb551bf7   Stefan Richter   firewire: normali...
147
148
149
150
151
152
153
154
155
  		client_put(r->client);
  }
  
  static void schedule_if_iso_resource(struct client_resource *resource)
  {
  	if (resource->release == release_iso_resource)
  		schedule_iso_resource(container_of(resource,
  					struct iso_resource, resource), 0);
  }
97c18b7fd   Stefan Richter   firewire: cdev: u...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
  /*
   * dequeue_event() just kfree()'s the event, so the event has to be
   * the first field in a struct XYZ_event.
   */
  struct event {
  	struct { void *data; size_t size; } v[2];
  	struct list_head link;
  };
  
  struct bus_reset_event {
  	struct event event;
  	struct fw_cdev_event_bus_reset reset;
  };
  
  struct outbound_transaction_event {
  	struct event event;
  	struct client *client;
  	struct outbound_transaction_resource r;
  	struct fw_cdev_event_response response;
  };
  
  struct inbound_transaction_event {
  	struct event event;
e205597d1   Stefan Richter   firewire: cdev: f...
179
180
181
182
  	union {
  		struct fw_cdev_event_request request;
  		struct fw_cdev_event_request2 request2;
  	} req;
97c18b7fd   Stefan Richter   firewire: cdev: u...
183
184
185
186
187
188
  };
  
  struct iso_interrupt_event {
  	struct event event;
  	struct fw_cdev_event_iso_interrupt interrupt;
  };
872e330e3   Stefan Richter   firewire: add iso...
189
190
191
192
  struct iso_interrupt_mc_event {
  	struct event event;
  	struct fw_cdev_event_iso_interrupt_mc interrupt;
  };
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
193
194
  struct iso_resource_event {
  	struct event event;
e21fcf798   Stefan Richter   firewire: cdev: n...
195
  	struct fw_cdev_event_iso_resource iso_resource;
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
196
  };
850bb6f23   Stefan Richter   firewire: cdev: a...
197
198
199
200
201
202
  struct outbound_phy_packet_event {
  	struct event event;
  	struct client *client;
  	struct fw_packet p;
  	struct fw_cdev_event_phy_packet phy_packet;
  };
bf54e1462   Stefan Richter   firewire: cdev: a...
203
204
205
206
  struct inbound_phy_packet_event {
  	struct event event;
  	struct fw_cdev_event_phy_packet phy_packet;
  };
9c1176b6a   Stefan Richter   firewire: cdev: f...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  #ifdef CONFIG_COMPAT
  static void __user *u64_to_uptr(u64 value)
  {
  	if (is_compat_task())
  		return compat_ptr(value);
  	else
  		return (void __user *)(unsigned long)value;
  }
  
  static u64 uptr_to_u64(void __user *ptr)
  {
  	if (is_compat_task())
  		return ptr_to_compat(ptr);
  	else
  		return (u64)(unsigned long)ptr;
  }
  #else
  static inline void __user *u64_to_uptr(u64 value)
19a15b937   Kristian Høgsberg   firewire: Add dev...
225
226
227
  {
  	return (void __user *)(unsigned long)value;
  }
9c1176b6a   Stefan Richter   firewire: cdev: f...
228
  static inline u64 uptr_to_u64(void __user *ptr)
19a15b937   Kristian Høgsberg   firewire: Add dev...
229
  {
9c1176b6a   Stefan Richter   firewire: cdev: f...
230
  	return (u64)(unsigned long)ptr;
19a15b937   Kristian Høgsberg   firewire: Add dev...
231
  }
9c1176b6a   Stefan Richter   firewire: cdev: f...
232
  #endif /* CONFIG_COMPAT */
19a15b937   Kristian Høgsberg   firewire: Add dev...
233
234
235
236
237
  
  static int fw_device_op_open(struct inode *inode, struct file *file)
  {
  	struct fw_device *device;
  	struct client *client;
96b19062e   Stefan Richter   firewire: fix "ko...
238
  	device = fw_device_get_by_devt(inode->i_rdev);
a3aca3dab   Kristian Høgsberg   firewire: Switch ...
239
240
  	if (device == NULL)
  		return -ENODEV;
19a15b937   Kristian Høgsberg   firewire: Add dev...
241

551f4cb9d   Jay Fenlason   firewire: prevent...
242
243
244
245
  	if (fw_device_is_shutdown(device)) {
  		fw_device_put(device);
  		return -ENODEV;
  	}
2d826cc5c   Kristian Høgsberg   firewire: Always ...
246
  	client = kzalloc(sizeof(*client), GFP_KERNEL);
96b19062e   Stefan Richter   firewire: fix "ko...
247
248
  	if (client == NULL) {
  		fw_device_put(device);
19a15b937   Kristian Høgsberg   firewire: Add dev...
249
  		return -ENOMEM;
96b19062e   Stefan Richter   firewire: fix "ko...
250
  	}
19a15b937   Kristian Høgsberg   firewire: Add dev...
251

96b19062e   Stefan Richter   firewire: fix "ko...
252
  	client->device = device;
19a15b937   Kristian Høgsberg   firewire: Add dev...
253
  	spin_lock_init(&client->lock);
45ee3199e   Jay Fenlason   firewire: cdev: u...
254
255
  	idr_init(&client->resource_idr);
  	INIT_LIST_HEAD(&client->event_list);
19a15b937   Kristian Høgsberg   firewire: Add dev...
256
  	init_waitqueue_head(&client->wait);
5a5e62da9   Clemens Ladisch   firewire: cdev: a...
257
  	init_waitqueue_head(&client->tx_flush_wait);
bf54e1462   Stefan Richter   firewire: cdev: a...
258
  	INIT_LIST_HEAD(&client->phy_receiver_link);
93b37905f   Stefan Richter   firewire: cdev: p...
259
  	INIT_LIST_HEAD(&client->link);
fb4430367   Stefan Richter   firewire: cdev: r...
260
  	kref_init(&client->kref);
19a15b937   Kristian Høgsberg   firewire: Add dev...
261
262
  
  	file->private_data = client;
3ac26b2ee   Stefan Richter   firewire: cdev: m...
263
  	return nonseekable_open(inode, file);
19a15b937   Kristian Høgsberg   firewire: Add dev...
264
265
266
267
268
269
270
271
272
273
274
275
276
  }
  
  static void queue_event(struct client *client, struct event *event,
  			void *data0, size_t size0, void *data1, size_t size1)
  {
  	unsigned long flags;
  
  	event->v[0].data = data0;
  	event->v[0].size = size0;
  	event->v[1].data = data1;
  	event->v[1].size = size1;
  
  	spin_lock_irqsave(&client->lock, flags);
45ee3199e   Jay Fenlason   firewire: cdev: u...
277
278
279
280
  	if (client->in_shutdown)
  		kfree(event);
  	else
  		list_add_tail(&event->link, &client->event_list);
19a15b937   Kristian Høgsberg   firewire: Add dev...
281
  	spin_unlock_irqrestore(&client->lock, flags);
83431cba3   Jay Fenlason   firewire: fw-cdev...
282
283
  
  	wake_up_interruptible(&client->wait);
19a15b937   Kristian Høgsberg   firewire: Add dev...
284
  }
53dca5117   Stefan Richter   firewire: remove ...
285
286
  static int dequeue_event(struct client *client,
  			 char __user *buffer, size_t count)
19a15b937   Kristian Høgsberg   firewire: Add dev...
287
  {
19a15b937   Kristian Høgsberg   firewire: Add dev...
288
289
  	struct event *event;
  	size_t size, total;
2dbd7d7e2   Stefan Richter   firewire: standar...
290
  	int i, ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
291

2dbd7d7e2   Stefan Richter   firewire: standar...
292
293
294
295
296
  	ret = wait_event_interruptible(client->wait,
  			!list_empty(&client->event_list) ||
  			fw_device_is_shutdown(client->device));
  	if (ret < 0)
  		return ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
297

2603bf219   Kristian Høgsberg   firewire: Use onl...
298
299
300
  	if (list_empty(&client->event_list) &&
  		       fw_device_is_shutdown(client->device))
  		return -ENODEV;
19a15b937   Kristian Høgsberg   firewire: Add dev...
301

3ba949868   Stefan Richter   firewire: cdev: r...
302
  	spin_lock_irq(&client->lock);
a459b8ab9   Stefan Richter   firewire: cdev: u...
303
  	event = list_first_entry(&client->event_list, struct event, link);
19a15b937   Kristian Høgsberg   firewire: Add dev...
304
  	list_del(&event->link);
3ba949868   Stefan Richter   firewire: cdev: r...
305
  	spin_unlock_irq(&client->lock);
19a15b937   Kristian Høgsberg   firewire: Add dev...
306

19a15b937   Kristian Høgsberg   firewire: Add dev...
307
308
309
  	total = 0;
  	for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
  		size = min(event->v[i].size, count - total);
2603bf219   Kristian Høgsberg   firewire: Use onl...
310
  		if (copy_to_user(buffer + total, event->v[i].data, size)) {
2dbd7d7e2   Stefan Richter   firewire: standar...
311
  			ret = -EFAULT;
19a15b937   Kristian Høgsberg   firewire: Add dev...
312
  			goto out;
2603bf219   Kristian Høgsberg   firewire: Use onl...
313
  		}
19a15b937   Kristian Høgsberg   firewire: Add dev...
314
315
  		total += size;
  	}
2dbd7d7e2   Stefan Richter   firewire: standar...
316
  	ret = total;
19a15b937   Kristian Høgsberg   firewire: Add dev...
317
318
319
  
   out:
  	kfree(event);
2dbd7d7e2   Stefan Richter   firewire: standar...
320
  	return ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
321
  }
53dca5117   Stefan Richter   firewire: remove ...
322
323
  static ssize_t fw_device_op_read(struct file *file, char __user *buffer,
  				 size_t count, loff_t *offset)
19a15b937   Kristian Høgsberg   firewire: Add dev...
324
325
326
327
328
  {
  	struct client *client = file->private_data;
  
  	return dequeue_event(client, buffer, count);
  }
53dca5117   Stefan Richter   firewire: remove ...
329
330
  static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
  				 struct client *client)
344bbc4de   Kristian Høgsberg   firewire: General...
331
  {
da8ecffae   Kristian Høgsberg   firewire: Streaml...
332
  	struct fw_card *card = client->device->card;
cf417e549   Jay Fenlason   firewire: add a c...
333

3ba949868   Stefan Richter   firewire: cdev: r...
334
  	spin_lock_irq(&card->lock);
344bbc4de   Kristian Høgsberg   firewire: General...
335

da8ecffae   Kristian Høgsberg   firewire: Streaml...
336
  	event->closure	     = client->bus_reset_closure;
344bbc4de   Kristian Høgsberg   firewire: General...
337
  	event->type          = FW_CDEV_EVENT_BUS_RESET;
cf5a56ac8   Stefan Richter   firewire: fw-cdev...
338
  	event->generation    = client->device->generation;
da8ecffae   Kristian Høgsberg   firewire: Streaml...
339
  	event->node_id       = client->device->node_id;
344bbc4de   Kristian Høgsberg   firewire: General...
340
  	event->local_node_id = card->local_node->node_id;
250b2b6dd   Stefan Richter   firewire: cdev: f...
341
  	event->bm_node_id    = card->bm_node_id;
344bbc4de   Kristian Høgsberg   firewire: General...
342
343
  	event->irm_node_id   = card->irm_node->node_id;
  	event->root_node_id  = card->root_node->node_id;
cf417e549   Jay Fenlason   firewire: add a c...
344

3ba949868   Stefan Richter   firewire: cdev: r...
345
  	spin_unlock_irq(&card->lock);
344bbc4de   Kristian Høgsberg   firewire: General...
346
  }
53dca5117   Stefan Richter   firewire: remove ...
347
348
  static void for_each_client(struct fw_device *device,
  			    void (*callback)(struct client *client))
2603bf219   Kristian Høgsberg   firewire: Use onl...
349
  {
2603bf219   Kristian Høgsberg   firewire: Use onl...
350
  	struct client *c;
2603bf219   Kristian Høgsberg   firewire: Use onl...
351

d67cfb961   Stefan Richter   firewire: convert...
352
  	mutex_lock(&device->client_list_mutex);
2603bf219   Kristian Høgsberg   firewire: Use onl...
353
354
  	list_for_each_entry(c, &device->client_list, link)
  		callback(c);
d67cfb961   Stefan Richter   firewire: convert...
355
  	mutex_unlock(&device->client_list_mutex);
2603bf219   Kristian Høgsberg   firewire: Use onl...
356
  }
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
357
358
  static int schedule_reallocations(int id, void *p, void *data)
  {
9fb551bf7   Stefan Richter   firewire: normali...
359
  	schedule_if_iso_resource(p);
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
360

b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
361
362
  	return 0;
  }
53dca5117   Stefan Richter   firewire: remove ...
363
  static void queue_bus_reset_event(struct client *client)
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
364
  {
97c18b7fd   Stefan Richter   firewire: cdev: u...
365
  	struct bus_reset_event *e;
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
366

97c18b7fd   Stefan Richter   firewire: cdev: u...
367
  	e = kzalloc(sizeof(*e), GFP_KERNEL);
cfb0c9d1f   Stefan Richter   firewire: remove ...
368
  	if (e == NULL)
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
369
  		return;
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
370

97c18b7fd   Stefan Richter   firewire: cdev: u...
371
  	fill_bus_reset_event(&e->reset, client);
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
372

97c18b7fd   Stefan Richter   firewire: cdev: u...
373
374
  	queue_event(client, &e->event,
  		    &e->reset, sizeof(e->reset), NULL, 0);
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
375
376
377
378
  
  	spin_lock_irq(&client->lock);
  	idr_for_each(&client->resource_idr, schedule_reallocations, client);
  	spin_unlock_irq(&client->lock);
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
379
380
381
382
  }
  
  void fw_device_cdev_update(struct fw_device *device)
  {
2603bf219   Kristian Høgsberg   firewire: Use onl...
383
384
  	for_each_client(device, queue_bus_reset_event);
  }
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
385

2603bf219   Kristian Høgsberg   firewire: Use onl...
386
387
388
389
  static void wake_up_client(struct client *client)
  {
  	wake_up_interruptible(&client->wait);
  }
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
390

2603bf219   Kristian Høgsberg   firewire: Use onl...
391
392
393
  void fw_device_cdev_remove(struct fw_device *device)
  {
  	for_each_client(device, wake_up_client);
97bd9efa5   Kristian Høgsberg   firewire: Add a b...
394
  }
6e95dea72   Stefan Richter   firewire: core: c...
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
  union ioctl_arg {
  	struct fw_cdev_get_info			get_info;
  	struct fw_cdev_send_request		send_request;
  	struct fw_cdev_allocate			allocate;
  	struct fw_cdev_deallocate		deallocate;
  	struct fw_cdev_send_response		send_response;
  	struct fw_cdev_initiate_bus_reset	initiate_bus_reset;
  	struct fw_cdev_add_descriptor		add_descriptor;
  	struct fw_cdev_remove_descriptor	remove_descriptor;
  	struct fw_cdev_create_iso_context	create_iso_context;
  	struct fw_cdev_queue_iso		queue_iso;
  	struct fw_cdev_start_iso		start_iso;
  	struct fw_cdev_stop_iso			stop_iso;
  	struct fw_cdev_get_cycle_timer		get_cycle_timer;
  	struct fw_cdev_allocate_iso_resource	allocate_iso_resource;
  	struct fw_cdev_send_stream_packet	send_stream_packet;
  	struct fw_cdev_get_cycle_timer2		get_cycle_timer2;
850bb6f23   Stefan Richter   firewire: cdev: a...
412
  	struct fw_cdev_send_phy_packet		send_phy_packet;
bf54e1462   Stefan Richter   firewire: cdev: a...
413
  	struct fw_cdev_receive_phy_packets	receive_phy_packets;
872e330e3   Stefan Richter   firewire: add iso...
414
  	struct fw_cdev_set_iso_channels		set_iso_channels;
d1bbd2097   Clemens Ladisch   firewire: allow e...
415
  	struct fw_cdev_flush_iso		flush_iso;
6e95dea72   Stefan Richter   firewire: core: c...
416
417
418
  };
  
  static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
19a15b937   Kristian Høgsberg   firewire: Add dev...
419
  {
6e95dea72   Stefan Richter   firewire: core: c...
420
  	struct fw_cdev_get_info *a = &arg->get_info;
344bbc4de   Kristian Høgsberg   firewire: General...
421
  	struct fw_cdev_event_bus_reset bus_reset;
c9755e14a   Stefan Richter   firewire: reread ...
422
  	unsigned long ret = 0;
344bbc4de   Kristian Høgsberg   firewire: General...
423

6e95dea72   Stefan Richter   firewire: core: c...
424
  	client->version = a->version;
604f45167   Stefan Richter   firewire: cdev: f...
425
  	a->version = FW_CDEV_KERNEL_VERSION;
6e95dea72   Stefan Richter   firewire: core: c...
426
  	a->card = client->device->card->index;
344bbc4de   Kristian Høgsberg   firewire: General...
427

c9755e14a   Stefan Richter   firewire: reread ...
428
  	down_read(&fw_device_rwsem);
6e95dea72   Stefan Richter   firewire: core: c...
429
430
  	if (a->rom != 0) {
  		size_t want = a->rom_length;
d84702a5d   Stefan Richter   firewire: fix com...
431
  		size_t have = client->device->config_rom_length * 4;
344bbc4de   Kristian Høgsberg   firewire: General...
432

6e95dea72   Stefan Richter   firewire: core: c...
433
434
  		ret = copy_to_user(u64_to_uptr(a->rom),
  				   client->device->config_rom, min(want, have));
344bbc4de   Kristian Høgsberg   firewire: General...
435
  	}
6e95dea72   Stefan Richter   firewire: core: c...
436
  	a->rom_length = client->device->config_rom_length * 4;
344bbc4de   Kristian Høgsberg   firewire: General...
437

c9755e14a   Stefan Richter   firewire: reread ...
438
439
440
441
  	up_read(&fw_device_rwsem);
  
  	if (ret != 0)
  		return -EFAULT;
93b37905f   Stefan Richter   firewire: cdev: p...
442
  	mutex_lock(&client->device->client_list_mutex);
6e95dea72   Stefan Richter   firewire: core: c...
443
444
  	client->bus_reset_closure = a->bus_reset_closure;
  	if (a->bus_reset != 0) {
da8ecffae   Kristian Høgsberg   firewire: Streaml...
445
  		fill_bus_reset_event(&bus_reset, client);
790198f74   Stefan Richter   firewire: cdev: f...
446
447
  		/* unaligned size of bus_reset is 36 bytes */
  		ret = copy_to_user(u64_to_uptr(a->bus_reset), &bus_reset, 36);
344bbc4de   Kristian Høgsberg   firewire: General...
448
  	}
93b37905f   Stefan Richter   firewire: cdev: p...
449
450
  	if (ret == 0 && list_empty(&client->link))
  		list_add_tail(&client->link, &client->device->client_list);
19a15b937   Kristian Høgsberg   firewire: Add dev...
451

93b37905f   Stefan Richter   firewire: cdev: p...
452
453
454
  	mutex_unlock(&client->device->client_list_mutex);
  
  	return ret ? -EFAULT : 0;
19a15b937   Kristian Høgsberg   firewire: Add dev...
455
  }
53dca5117   Stefan Richter   firewire: remove ...
456
457
  static int add_client_resource(struct client *client,
  			       struct client_resource *resource, gfp_t gfp_mask)
3964a4496   Kristian Høgsberg   firewire: General...
458
  {
37b61890d   Tejun Heo   firewire: convert...
459
  	bool preload = gfp_mask & __GFP_WAIT;
3964a4496   Kristian Høgsberg   firewire: General...
460
  	unsigned long flags;
45ee3199e   Jay Fenlason   firewire: cdev: u...
461
  	int ret;
37b61890d   Tejun Heo   firewire: convert...
462
463
  	if (preload)
  		idr_preload(gfp_mask);
3964a4496   Kristian Høgsberg   firewire: General...
464
  	spin_lock_irqsave(&client->lock, flags);
37b61890d   Tejun Heo   firewire: convert...
465

45ee3199e   Jay Fenlason   firewire: cdev: u...
466
467
468
  	if (client->in_shutdown)
  		ret = -ECANCELED;
  	else
37b61890d   Tejun Heo   firewire: convert...
469
470
  		ret = idr_alloc(&client->resource_idr, resource, 0, 0,
  				GFP_NOWAIT);
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
471
  	if (ret >= 0) {
37b61890d   Tejun Heo   firewire: convert...
472
  		resource->handle = ret;
fb4430367   Stefan Richter   firewire: cdev: r...
473
  		client_get(client);
9fb551bf7   Stefan Richter   firewire: normali...
474
  		schedule_if_iso_resource(resource);
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
475
  	}
45ee3199e   Jay Fenlason   firewire: cdev: u...
476

37b61890d   Tejun Heo   firewire: convert...
477
478
479
  	spin_unlock_irqrestore(&client->lock, flags);
  	if (preload)
  		idr_preload_end();
45ee3199e   Jay Fenlason   firewire: cdev: u...
480
481
  
  	return ret < 0 ? ret : 0;
3964a4496   Kristian Høgsberg   firewire: General...
482
  }
53dca5117   Stefan Richter   firewire: remove ...
483
484
  static int release_client_resource(struct client *client, u32 handle,
  				   client_resource_release_fn_t release,
e21fcf798   Stefan Richter   firewire: cdev: n...
485
  				   struct client_resource **return_resource)
3964a4496   Kristian Høgsberg   firewire: General...
486
  {
e21fcf798   Stefan Richter   firewire: cdev: n...
487
  	struct client_resource *resource;
3964a4496   Kristian Høgsberg   firewire: General...
488

3ba949868   Stefan Richter   firewire: cdev: r...
489
  	spin_lock_irq(&client->lock);
45ee3199e   Jay Fenlason   firewire: cdev: u...
490
  	if (client->in_shutdown)
e21fcf798   Stefan Richter   firewire: cdev: n...
491
  		resource = NULL;
45ee3199e   Jay Fenlason   firewire: cdev: u...
492
  	else
e21fcf798   Stefan Richter   firewire: cdev: n...
493
494
  		resource = idr_find(&client->resource_idr, handle);
  	if (resource && resource->release == release)
45ee3199e   Jay Fenlason   firewire: cdev: u...
495
  		idr_remove(&client->resource_idr, handle);
3ba949868   Stefan Richter   firewire: cdev: r...
496
  	spin_unlock_irq(&client->lock);
3964a4496   Kristian Høgsberg   firewire: General...
497

e21fcf798   Stefan Richter   firewire: cdev: n...
498
  	if (!(resource && resource->release == release))
3964a4496   Kristian Høgsberg   firewire: General...
499
  		return -EINVAL;
e21fcf798   Stefan Richter   firewire: cdev: n...
500
501
  	if (return_resource)
  		*return_resource = resource;
3964a4496   Kristian Høgsberg   firewire: General...
502
  	else
e21fcf798   Stefan Richter   firewire: cdev: n...
503
  		resource->release(client, resource);
3964a4496   Kristian Høgsberg   firewire: General...
504

fb4430367   Stefan Richter   firewire: cdev: r...
505
  	client_put(client);
3964a4496   Kristian Høgsberg   firewire: General...
506
507
  	return 0;
  }
53dca5117   Stefan Richter   firewire: remove ...
508
509
  static void release_transaction(struct client *client,
  				struct client_resource *resource)
3964a4496   Kristian Høgsberg   firewire: General...
510
  {
3964a4496   Kristian Høgsberg   firewire: General...
511
  }
53dca5117   Stefan Richter   firewire: remove ...
512
513
  static void complete_transaction(struct fw_card *card, int rcode,
  				 void *payload, size_t length, void *data)
19a15b937   Kristian Høgsberg   firewire: Add dev...
514
  {
97c18b7fd   Stefan Richter   firewire: cdev: u...
515
516
517
  	struct outbound_transaction_event *e = data;
  	struct fw_cdev_event_response *rsp = &e->response;
  	struct client *client = e->client;
28cf6a04c   Kristian Høgsberg   firewire: Track p...
518
  	unsigned long flags;
19a15b937   Kristian Høgsberg   firewire: Add dev...
519

97c18b7fd   Stefan Richter   firewire: cdev: u...
520
521
  	if (length < rsp->length)
  		rsp->length = length;
19a15b937   Kristian Høgsberg   firewire: Add dev...
522
  	if (rcode == RCODE_COMPLETE)
97c18b7fd   Stefan Richter   firewire: cdev: u...
523
  		memcpy(rsp->data, payload, rsp->length);
19a15b937   Kristian Høgsberg   firewire: Add dev...
524

28cf6a04c   Kristian Høgsberg   firewire: Track p...
525
  	spin_lock_irqsave(&client->lock, flags);
5a5e62da9   Clemens Ladisch   firewire: cdev: a...
526
527
528
  	idr_remove(&client->resource_idr, e->r.resource.handle);
  	if (client->in_shutdown)
  		wake_up(&client->tx_flush_wait);
28cf6a04c   Kristian Høgsberg   firewire: Track p...
529
  	spin_unlock_irqrestore(&client->lock, flags);
97c18b7fd   Stefan Richter   firewire: cdev: u...
530
531
  	rsp->type = FW_CDEV_EVENT_RESPONSE;
  	rsp->rcode = rcode;
8401d92ba   David Moore   firewire: Preserv...
532
533
  
  	/*
97c18b7fd   Stefan Richter   firewire: cdev: u...
534
  	 * In the case that sizeof(*rsp) doesn't align with the position of the
8401d92ba   David Moore   firewire: Preserv...
535
536
537
538
539
  	 * data, and the read is short, preserve an extra copy of the data
  	 * to stay compatible with a pre-2.6.27 bug.  Since the bug is harmless
  	 * for short reads and some apps depended on it, this is both safe
  	 * and prudent for compatibility.
  	 */
97c18b7fd   Stefan Richter   firewire: cdev: u...
540
541
542
  	if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data))
  		queue_event(client, &e->event, rsp, sizeof(*rsp),
  			    rsp->data, rsp->length);
8401d92ba   David Moore   firewire: Preserv...
543
  	else
97c18b7fd   Stefan Richter   firewire: cdev: u...
544
  		queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length,
8401d92ba   David Moore   firewire: Preserv...
545
  			    NULL, 0);
fb4430367   Stefan Richter   firewire: cdev: r...
546

5a5e62da9   Clemens Ladisch   firewire: cdev: a...
547
548
  	/* Drop the idr's reference */
  	client_put(client);
19a15b937   Kristian Høgsberg   firewire: Add dev...
549
  }
acfe83335   Jay Fenlason, Stefan Richter   firewire: cdev: a...
550
551
552
  static int init_request(struct client *client,
  			struct fw_cdev_send_request *request,
  			int destination_id, int speed)
19a15b937   Kristian Høgsberg   firewire: Add dev...
553
  {
97c18b7fd   Stefan Richter   firewire: cdev: u...
554
  	struct outbound_transaction_event *e;
1f3125af8   Stefan Richter   firewire: cdev: t...
555
  	int ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
556

18e9b10fc   Stefan Richter   firewire: cdev: a...
557
558
  	if (request->tcode != TCODE_STREAM_DATA &&
  	    (request->length > 4096 || request->length > 512 << speed))
5d3fd692a   Stefan Richter   firewire: cdev: e...
559
  		return -EIO;
19a15b937   Kristian Høgsberg   firewire: Add dev...
560

a8e93f3dc   Clemens Ladisch   firewire: cdev: c...
561
562
563
  	if (request->tcode == TCODE_WRITE_QUADLET_REQUEST &&
  	    request->length < 4)
  		return -EINVAL;
97c18b7fd   Stefan Richter   firewire: cdev: u...
564
565
  	e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
  	if (e == NULL)
19a15b937   Kristian Høgsberg   firewire: Add dev...
566
  		return -ENOMEM;
97c18b7fd   Stefan Richter   firewire: cdev: u...
567
568
569
  	e->client = client;
  	e->response.length = request->length;
  	e->response.closure = request->closure;
19a15b937   Kristian Høgsberg   firewire: Add dev...
570

4f2592232   Kristian Høgsberg   firewire: Add rea...
571
  	if (request->data &&
97c18b7fd   Stefan Richter   firewire: cdev: u...
572
  	    copy_from_user(e->response.data,
4f2592232   Kristian Høgsberg   firewire: Add rea...
573
  			   u64_to_uptr(request->data), request->length)) {
1f3125af8   Stefan Richter   firewire: cdev: t...
574
  		ret = -EFAULT;
45ee3199e   Jay Fenlason   firewire: cdev: u...
575
  		goto failed;
1f3125af8   Stefan Richter   firewire: cdev: t...
576
  	}
97c18b7fd   Stefan Richter   firewire: cdev: u...
577
578
  	e->r.resource.release = release_transaction;
  	ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
45ee3199e   Jay Fenlason   firewire: cdev: u...
579
580
  	if (ret < 0)
  		goto failed;
28cf6a04c   Kristian Høgsberg   firewire: Track p...
581

acfe83335   Jay Fenlason, Stefan Richter   firewire: cdev: a...
582
  	fw_send_request(client->device->card, &e->r.transaction,
664d8010b   Stefan Richter   firewire: cdev: s...
583
584
585
586
  			request->tcode, destination_id, request->generation,
  			speed, request->offset, e->response.data,
  			request->length, complete_transaction, e);
  	return 0;
19a15b937   Kristian Høgsberg   firewire: Add dev...
587

45ee3199e   Jay Fenlason   firewire: cdev: u...
588
   failed:
97c18b7fd   Stefan Richter   firewire: cdev: u...
589
  	kfree(e);
1f3125af8   Stefan Richter   firewire: cdev: t...
590
591
  
  	return ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
592
  }
6e95dea72   Stefan Richter   firewire: core: c...
593
  static int ioctl_send_request(struct client *client, union ioctl_arg *arg)
acfe83335   Jay Fenlason, Stefan Richter   firewire: cdev: a...
594
  {
6e95dea72   Stefan Richter   firewire: core: c...
595
  	switch (arg->send_request.tcode) {
acfe83335   Jay Fenlason, Stefan Richter   firewire: cdev: a...
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
  	case TCODE_WRITE_QUADLET_REQUEST:
  	case TCODE_WRITE_BLOCK_REQUEST:
  	case TCODE_READ_QUADLET_REQUEST:
  	case TCODE_READ_BLOCK_REQUEST:
  	case TCODE_LOCK_MASK_SWAP:
  	case TCODE_LOCK_COMPARE_SWAP:
  	case TCODE_LOCK_FETCH_ADD:
  	case TCODE_LOCK_LITTLE_ADD:
  	case TCODE_LOCK_BOUNDED_ADD:
  	case TCODE_LOCK_WRAP_ADD:
  	case TCODE_LOCK_VENDOR_DEPENDENT:
  		break;
  	default:
  		return -EINVAL;
  	}
6e95dea72   Stefan Richter   firewire: core: c...
611
  	return init_request(client, &arg->send_request, client->device->node_id,
acfe83335   Jay Fenlason, Stefan Richter   firewire: cdev: a...
612
613
  			    client->device->max_speed);
  }
281e20323   Stefan Richter   firewire: core: f...
614
615
616
617
  static inline bool is_fcp_request(struct fw_request *request)
  {
  	return request == NULL;
  }
53dca5117   Stefan Richter   firewire: remove ...
618
619
  static void release_request(struct client *client,
  			    struct client_resource *resource)
3964a4496   Kristian Høgsberg   firewire: General...
620
  {
97c18b7fd   Stefan Richter   firewire: cdev: u...
621
622
  	struct inbound_transaction_resource *r = container_of(resource,
  			struct inbound_transaction_resource, resource);
3964a4496   Kristian Høgsberg   firewire: General...
623

281e20323   Stefan Richter   firewire: core: f...
624
625
626
  	if (is_fcp_request(r->request))
  		kfree(r->data);
  	else
08bd34c98   Jay Fenlason   firewire: cdev: f...
627
  		fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR);
0244f5730   Stefan Richter   firewire: cdev: c...
628
629
  
  	fw_card_put(r->card);
97c18b7fd   Stefan Richter   firewire: cdev: u...
630
  	kfree(r);
3964a4496   Kristian Høgsberg   firewire: General...
631
  }
97c18b7fd   Stefan Richter   firewire: cdev: u...
632
  static void handle_request(struct fw_card *card, struct fw_request *request,
53dca5117   Stefan Richter   firewire: remove ...
633
  			   int tcode, int destination, int source,
33e553fe2   Stefan Richter   firewire: remove ...
634
  			   int generation, unsigned long long offset,
53dca5117   Stefan Richter   firewire: remove ...
635
  			   void *payload, size_t length, void *callback_data)
19a15b937   Kristian Høgsberg   firewire: Add dev...
636
  {
97c18b7fd   Stefan Richter   firewire: cdev: u...
637
638
639
  	struct address_handler_resource *handler = callback_data;
  	struct inbound_transaction_resource *r;
  	struct inbound_transaction_event *e;
e205597d1   Stefan Richter   firewire: cdev: f...
640
  	size_t event_size0;
281e20323   Stefan Richter   firewire: core: f...
641
  	void *fcp_frame = NULL;
45ee3199e   Jay Fenlason   firewire: cdev: u...
642
  	int ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
643

0244f5730   Stefan Richter   firewire: cdev: c...
644
645
  	/* card may be different from handler->client->device->card */
  	fw_card_get(card);
97c18b7fd   Stefan Richter   firewire: cdev: u...
646
  	r = kmalloc(sizeof(*r), GFP_ATOMIC);
2d826cc5c   Kristian Høgsberg   firewire: Always ...
647
  	e = kmalloc(sizeof(*e), GFP_ATOMIC);
cfb0c9d1f   Stefan Richter   firewire: remove ...
648
  	if (r == NULL || e == NULL)
45ee3199e   Jay Fenlason   firewire: cdev: u...
649
  		goto failed;
cfb0c9d1f   Stefan Richter   firewire: remove ...
650

08bd34c98   Jay Fenlason   firewire: cdev: f...
651
  	r->card    = card;
97c18b7fd   Stefan Richter   firewire: cdev: u...
652
653
654
  	r->request = request;
  	r->data    = payload;
  	r->length  = length;
19a15b937   Kristian Høgsberg   firewire: Add dev...
655

281e20323   Stefan Richter   firewire: core: f...
656
657
658
659
660
661
662
663
664
665
666
  	if (is_fcp_request(request)) {
  		/*
  		 * FIXME: Let core-transaction.c manage a
  		 * single reference-counted copy?
  		 */
  		fcp_frame = kmemdup(payload, length, GFP_ATOMIC);
  		if (fcp_frame == NULL)
  			goto failed;
  
  		r->data = fcp_frame;
  	}
97c18b7fd   Stefan Richter   firewire: cdev: u...
667
668
  	r->resource.release = release_request;
  	ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC);
45ee3199e   Jay Fenlason   firewire: cdev: u...
669
670
  	if (ret < 0)
  		goto failed;
19a15b937   Kristian Høgsberg   firewire: Add dev...
671

e205597d1   Stefan Richter   firewire: cdev: f...
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
  	if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) {
  		struct fw_cdev_event_request *req = &e->req.request;
  
  		if (tcode & 0x10)
  			tcode = TCODE_LOCK_REQUEST;
  
  		req->type	= FW_CDEV_EVENT_REQUEST;
  		req->tcode	= tcode;
  		req->offset	= offset;
  		req->length	= length;
  		req->handle	= r->resource.handle;
  		req->closure	= handler->closure;
  		event_size0	= sizeof(*req);
  	} else {
  		struct fw_cdev_event_request2 *req = &e->req.request2;
  
  		req->type	= FW_CDEV_EVENT_REQUEST2;
  		req->tcode	= tcode;
  		req->offset	= offset;
  		req->source_node_id = source;
  		req->destination_node_id = destination;
  		req->card	= card->index;
  		req->generation	= generation;
  		req->length	= length;
  		req->handle	= r->resource.handle;
  		req->closure	= handler->closure;
  		event_size0	= sizeof(*req);
  	}
19a15b937   Kristian Høgsberg   firewire: Add dev...
700

97c18b7fd   Stefan Richter   firewire: cdev: u...
701
  	queue_event(handler->client, &e->event,
e205597d1   Stefan Richter   firewire: cdev: f...
702
  		    &e->req, event_size0, r->data, length);
45ee3199e   Jay Fenlason   firewire: cdev: u...
703
704
705
  	return;
  
   failed:
97c18b7fd   Stefan Richter   firewire: cdev: u...
706
  	kfree(r);
45ee3199e   Jay Fenlason   firewire: cdev: u...
707
  	kfree(e);
281e20323   Stefan Richter   firewire: core: f...
708
709
710
  	kfree(fcp_frame);
  
  	if (!is_fcp_request(request))
db5d247ae   Clemens Ladisch   firewire: fix use...
711
  		fw_send_response(card, request, RCODE_CONFLICT_ERROR);
0244f5730   Stefan Richter   firewire: cdev: c...
712
713
  
  	fw_card_put(card);
19a15b937   Kristian Høgsberg   firewire: Add dev...
714
  }
53dca5117   Stefan Richter   firewire: remove ...
715
716
  static void release_address_handler(struct client *client,
  				    struct client_resource *resource)
3964a4496   Kristian Høgsberg   firewire: General...
717
  {
97c18b7fd   Stefan Richter   firewire: cdev: u...
718
719
  	struct address_handler_resource *r =
  	    container_of(resource, struct address_handler_resource, resource);
3964a4496   Kristian Høgsberg   firewire: General...
720

97c18b7fd   Stefan Richter   firewire: cdev: u...
721
722
  	fw_core_remove_address_handler(&r->handler);
  	kfree(r);
3964a4496   Kristian Høgsberg   firewire: General...
723
  }
6e95dea72   Stefan Richter   firewire: core: c...
724
  static int ioctl_allocate(struct client *client, union ioctl_arg *arg)
19a15b937   Kristian Høgsberg   firewire: Add dev...
725
  {
6e95dea72   Stefan Richter   firewire: core: c...
726
  	struct fw_cdev_allocate *a = &arg->allocate;
97c18b7fd   Stefan Richter   firewire: cdev: u...
727
  	struct address_handler_resource *r;
19a15b937   Kristian Høgsberg   firewire: Add dev...
728
  	struct fw_address_region region;
45ee3199e   Jay Fenlason   firewire: cdev: u...
729
  	int ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
730

97c18b7fd   Stefan Richter   firewire: cdev: u...
731
732
  	r = kmalloc(sizeof(*r), GFP_KERNEL);
  	if (r == NULL)
19a15b937   Kristian Høgsberg   firewire: Add dev...
733
  		return -ENOMEM;
6e95dea72   Stefan Richter   firewire: core: c...
734
  	region.start = a->offset;
8e2b2b46e   Stefan Richter   firewire: cdev: i...
735
736
737
738
  	if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END)
  		region.end = a->offset + a->length;
  	else
  		region.end = a->region_end;
6e95dea72   Stefan Richter   firewire: core: c...
739
  	r->handler.length           = a->length;
97c18b7fd   Stefan Richter   firewire: cdev: u...
740
  	r->handler.address_callback = handle_request;
6e95dea72   Stefan Richter   firewire: core: c...
741
742
743
  	r->handler.callback_data    = r;
  	r->closure   = a->closure;
  	r->client    = client;
19a15b937   Kristian Høgsberg   firewire: Add dev...
744

97c18b7fd   Stefan Richter   firewire: cdev: u...
745
  	ret = fw_core_add_address_handler(&r->handler, &region);
3e0b5f0d7   Stefan Richter   firewire: cdev: a...
746
  	if (ret < 0) {
97c18b7fd   Stefan Richter   firewire: cdev: u...
747
  		kfree(r);
3e0b5f0d7   Stefan Richter   firewire: cdev: a...
748
  		return ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
749
  	}
8e2b2b46e   Stefan Richter   firewire: cdev: i...
750
  	a->offset = r->handler.offset;
19a15b937   Kristian Høgsberg   firewire: Add dev...
751

97c18b7fd   Stefan Richter   firewire: cdev: u...
752
753
  	r->resource.release = release_address_handler;
  	ret = add_client_resource(client, &r->resource, GFP_KERNEL);
45ee3199e   Jay Fenlason   firewire: cdev: u...
754
  	if (ret < 0) {
97c18b7fd   Stefan Richter   firewire: cdev: u...
755
  		release_address_handler(client, &r->resource);
45ee3199e   Jay Fenlason   firewire: cdev: u...
756
757
  		return ret;
  	}
6e95dea72   Stefan Richter   firewire: core: c...
758
  	a->handle = r->resource.handle;
19a15b937   Kristian Høgsberg   firewire: Add dev...
759
760
761
  
  	return 0;
  }
6e95dea72   Stefan Richter   firewire: core: c...
762
  static int ioctl_deallocate(struct client *client, union ioctl_arg *arg)
9472316b6   Kristian Høgsberg   firewire: Impleme...
763
  {
6e95dea72   Stefan Richter   firewire: core: c...
764
  	return release_client_resource(client, arg->deallocate.handle,
45ee3199e   Jay Fenlason   firewire: cdev: u...
765
  				       release_address_handler, NULL);
9472316b6   Kristian Høgsberg   firewire: Impleme...
766
  }
6e95dea72   Stefan Richter   firewire: core: c...
767
  static int ioctl_send_response(struct client *client, union ioctl_arg *arg)
19a15b937   Kristian Høgsberg   firewire: Add dev...
768
  {
6e95dea72   Stefan Richter   firewire: core: c...
769
  	struct fw_cdev_send_response *a = &arg->send_response;
3964a4496   Kristian Høgsberg   firewire: General...
770
  	struct client_resource *resource;
97c18b7fd   Stefan Richter   firewire: cdev: u...
771
  	struct inbound_transaction_resource *r;
7e44c0b56   Stefan Richter   firewire: cdev: f...
772
  	int ret = 0;
19a15b937   Kristian Høgsberg   firewire: Add dev...
773

6e95dea72   Stefan Richter   firewire: core: c...
774
  	if (release_client_resource(client, a->handle,
45ee3199e   Jay Fenlason   firewire: cdev: u...
775
  				    release_request, &resource) < 0)
19a15b937   Kristian Høgsberg   firewire: Add dev...
776
  		return -EINVAL;
45ee3199e   Jay Fenlason   firewire: cdev: u...
777

97c18b7fd   Stefan Richter   firewire: cdev: u...
778
779
  	r = container_of(resource, struct inbound_transaction_resource,
  			 resource);
281e20323   Stefan Richter   firewire: core: f...
780
781
  	if (is_fcp_request(r->request))
  		goto out;
a10c0ce76   Clemens Ladisch   firewire: check c...
782
783
784
785
786
787
  	if (a->length != fw_get_response_length(r->request)) {
  		ret = -EINVAL;
  		kfree(r->request);
  		goto out;
  	}
  	if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) {
281e20323   Stefan Richter   firewire: core: f...
788
789
790
  		ret = -EFAULT;
  		kfree(r->request);
  		goto out;
7e44c0b56   Stefan Richter   firewire: cdev: f...
791
  	}
08bd34c98   Jay Fenlason   firewire: cdev: f...
792
  	fw_send_response(r->card, r->request, a->rcode);
7e44c0b56   Stefan Richter   firewire: cdev: f...
793
   out:
0244f5730   Stefan Richter   firewire: cdev: c...
794
  	fw_card_put(r->card);
19a15b937   Kristian Høgsberg   firewire: Add dev...
795
  	kfree(r);
7e44c0b56   Stefan Richter   firewire: cdev: f...
796
  	return ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
797
  }
6e95dea72   Stefan Richter   firewire: core: c...
798
  static int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg)
5371842b7   Kristian Høgsberg   firewire: Impleme...
799
  {
02d37bed1   Stefan Richter   firewire: core: i...
800
  	fw_schedule_bus_reset(client->device->card, true,
6e95dea72   Stefan Richter   firewire: core: c...
801
  			arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET);
02d37bed1   Stefan Richter   firewire: core: i...
802
  	return 0;
5371842b7   Kristian Høgsberg   firewire: Impleme...
803
  }
3964a4496   Kristian Høgsberg   firewire: General...
804
805
806
  static void release_descriptor(struct client *client,
  			       struct client_resource *resource)
  {
97c18b7fd   Stefan Richter   firewire: cdev: u...
807
808
  	struct descriptor_resource *r =
  		container_of(resource, struct descriptor_resource, resource);
3964a4496   Kristian Høgsberg   firewire: General...
809

97c18b7fd   Stefan Richter   firewire: cdev: u...
810
811
  	fw_core_remove_descriptor(&r->descriptor);
  	kfree(r);
3964a4496   Kristian Høgsberg   firewire: General...
812
  }
6e95dea72   Stefan Richter   firewire: core: c...
813
  static int ioctl_add_descriptor(struct client *client, union ioctl_arg *arg)
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
814
  {
6e95dea72   Stefan Richter   firewire: core: c...
815
  	struct fw_cdev_add_descriptor *a = &arg->add_descriptor;
97c18b7fd   Stefan Richter   firewire: cdev: u...
816
  	struct descriptor_resource *r;
45ee3199e   Jay Fenlason   firewire: cdev: u...
817
  	int ret;
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
818

de487da8c   Stefan Richter   firewire: cdev: s...
819
  	/* Access policy: Allow this ioctl only on local nodes' device files. */
92368890d   Stefan Richter   firewire: core: i...
820
  	if (!client->device->is_local)
de487da8c   Stefan Richter   firewire: cdev: s...
821
  		return -ENOSYS;
6e95dea72   Stefan Richter   firewire: core: c...
822
  	if (a->length > 256)
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
823
  		return -EINVAL;
6e95dea72   Stefan Richter   firewire: core: c...
824
  	r = kmalloc(sizeof(*r) + a->length * 4, GFP_KERNEL);
97c18b7fd   Stefan Richter   firewire: cdev: u...
825
  	if (r == NULL)
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
826
  		return -ENOMEM;
6e95dea72   Stefan Richter   firewire: core: c...
827
  	if (copy_from_user(r->data, u64_to_uptr(a->data), a->length * 4)) {
45ee3199e   Jay Fenlason   firewire: cdev: u...
828
829
  		ret = -EFAULT;
  		goto failed;
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
830
  	}
6e95dea72   Stefan Richter   firewire: core: c...
831
832
833
  	r->descriptor.length    = a->length;
  	r->descriptor.immediate = a->immediate;
  	r->descriptor.key       = a->key;
97c18b7fd   Stefan Richter   firewire: cdev: u...
834
  	r->descriptor.data      = r->data;
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
835

97c18b7fd   Stefan Richter   firewire: cdev: u...
836
  	ret = fw_core_add_descriptor(&r->descriptor);
45ee3199e   Jay Fenlason   firewire: cdev: u...
837
838
  	if (ret < 0)
  		goto failed;
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
839

97c18b7fd   Stefan Richter   firewire: cdev: u...
840
841
  	r->resource.release = release_descriptor;
  	ret = add_client_resource(client, &r->resource, GFP_KERNEL);
45ee3199e   Jay Fenlason   firewire: cdev: u...
842
  	if (ret < 0) {
97c18b7fd   Stefan Richter   firewire: cdev: u...
843
  		fw_core_remove_descriptor(&r->descriptor);
45ee3199e   Jay Fenlason   firewire: cdev: u...
844
845
  		goto failed;
  	}
6e95dea72   Stefan Richter   firewire: core: c...
846
  	a->handle = r->resource.handle;
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
847
848
  
  	return 0;
45ee3199e   Jay Fenlason   firewire: cdev: u...
849
   failed:
97c18b7fd   Stefan Richter   firewire: cdev: u...
850
  	kfree(r);
45ee3199e   Jay Fenlason   firewire: cdev: u...
851
852
  
  	return ret;
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
853
  }
6e95dea72   Stefan Richter   firewire: core: c...
854
  static int ioctl_remove_descriptor(struct client *client, union ioctl_arg *arg)
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
855
  {
6e95dea72   Stefan Richter   firewire: core: c...
856
  	return release_client_resource(client, arg->remove_descriptor.handle,
45ee3199e   Jay Fenlason   firewire: cdev: u...
857
  				       release_descriptor, NULL);
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
858
  }
53dca5117   Stefan Richter   firewire: remove ...
859
860
  static void iso_callback(struct fw_iso_context *context, u32 cycle,
  			 size_t header_length, void *header, void *data)
19a15b937   Kristian Høgsberg   firewire: Add dev...
861
862
  {
  	struct client *client = data;
97c18b7fd   Stefan Richter   firewire: cdev: u...
863
  	struct iso_interrupt_event *e;
19a15b937   Kristian Høgsberg   firewire: Add dev...
864

56d04cb18   Stefan Richter   firewire: core: r...
865
  	e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC);
cfb0c9d1f   Stefan Richter   firewire: remove ...
866
  	if (e == NULL)
19a15b937   Kristian Høgsberg   firewire: Add dev...
867
  		return;
cfb0c9d1f   Stefan Richter   firewire: remove ...
868

97c18b7fd   Stefan Richter   firewire: cdev: u...
869
870
871
872
873
874
875
  	e->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
  	e->interrupt.closure   = client->iso_closure;
  	e->interrupt.cycle     = cycle;
  	e->interrupt.header_length = header_length;
  	memcpy(e->interrupt.header, header, header_length);
  	queue_event(client, &e->event, &e->interrupt,
  		    sizeof(e->interrupt) + header_length, NULL, 0);
19a15b937   Kristian Høgsberg   firewire: Add dev...
876
  }
872e330e3   Stefan Richter   firewire: add iso...
877
878
879
880
881
882
883
  static void iso_mc_callback(struct fw_iso_context *context,
  			    dma_addr_t completed, void *data)
  {
  	struct client *client = data;
  	struct iso_interrupt_mc_event *e;
  
  	e = kmalloc(sizeof(*e), GFP_ATOMIC);
cfb0c9d1f   Stefan Richter   firewire: remove ...
884
  	if (e == NULL)
872e330e3   Stefan Richter   firewire: add iso...
885
  		return;
cfb0c9d1f   Stefan Richter   firewire: remove ...
886

872e330e3   Stefan Richter   firewire: add iso...
887
888
889
890
891
892
893
  	e->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL;
  	e->interrupt.closure   = client->iso_closure;
  	e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer,
  						      completed);
  	queue_event(client, &e->event, &e->interrupt,
  		    sizeof(e->interrupt), NULL, 0);
  }
0b6c4857f   Stefan Richter   firewire: core: f...
894
895
896
897
898
899
900
  static enum dma_data_direction iso_dma_direction(struct fw_iso_context *context)
  {
  		if (context->type == FW_ISO_CONTEXT_TRANSMIT)
  			return DMA_TO_DEVICE;
  		else
  			return DMA_FROM_DEVICE;
  }
6e95dea72   Stefan Richter   firewire: core: c...
901
  static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
19a15b937   Kristian Høgsberg   firewire: Add dev...
902
  {
6e95dea72   Stefan Richter   firewire: core: c...
903
  	struct fw_cdev_create_iso_context *a = &arg->create_iso_context;
24315c5e6   Kristian Høgsberg   firewire: Only se...
904
  	struct fw_iso_context *context;
872e330e3   Stefan Richter   firewire: add iso...
905
  	fw_iso_callback_t cb;
0b6c4857f   Stefan Richter   firewire: core: f...
906
  	int ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
907

eb5b35a56   Stefan Richter   firewire: core: e...
908
  	BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
872e330e3   Stefan Richter   firewire: add iso...
909
910
911
  		     FW_CDEV_ISO_CONTEXT_RECEIVE  != FW_ISO_CONTEXT_RECEIVE  ||
  		     FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL !=
  					FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL);
21efb3cfc   Kristian Høgsberg   firewire: Configu...
912

6e95dea72   Stefan Richter   firewire: core: c...
913
  	switch (a->type) {
872e330e3   Stefan Richter   firewire: add iso...
914
915
  	case FW_ISO_CONTEXT_TRANSMIT:
  		if (a->speed > SCODE_3200 || a->channel > 63)
c70dc788f   Kristian Høgsberg   firewire: Fix dua...
916
  			return -EINVAL;
872e330e3   Stefan Richter   firewire: add iso...
917
918
  
  		cb = iso_callback;
c70dc788f   Kristian Høgsberg   firewire: Fix dua...
919
  		break;
872e330e3   Stefan Richter   firewire: add iso...
920
921
922
  	case FW_ISO_CONTEXT_RECEIVE:
  		if (a->header_size < 4 || (a->header_size & 3) ||
  		    a->channel > 63)
c70dc788f   Kristian Høgsberg   firewire: Fix dua...
923
  			return -EINVAL;
872e330e3   Stefan Richter   firewire: add iso...
924
925
926
927
928
929
  
  		cb = iso_callback;
  		break;
  
  	case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
  		cb = (fw_iso_callback_t)iso_mc_callback;
c70dc788f   Kristian Høgsberg   firewire: Fix dua...
930
931
932
  		break;
  
  	default:
21efb3cfc   Kristian Høgsberg   firewire: Configu...
933
  		return -EINVAL;
c70dc788f   Kristian Høgsberg   firewire: Fix dua...
934
  	}
6e95dea72   Stefan Richter   firewire: core: c...
935
  	context = fw_iso_context_create(client->device->card, a->type,
872e330e3   Stefan Richter   firewire: add iso...
936
  			a->channel, a->speed, a->header_size, cb, client);
24315c5e6   Kristian Høgsberg   firewire: Only se...
937
938
  	if (IS_ERR(context))
  		return PTR_ERR(context);
bdfe273ee   Clemens Ladisch   firewire: cdev: f...
939
940
941
942
943
  	/* We only support one context at this time. */
  	spin_lock_irq(&client->lock);
  	if (client->iso_context != NULL) {
  		spin_unlock_irq(&client->lock);
  		fw_iso_context_destroy(context);
0b6c4857f   Stefan Richter   firewire: core: f...
944

bdfe273ee   Clemens Ladisch   firewire: cdev: f...
945
946
  		return -EBUSY;
  	}
0b6c4857f   Stefan Richter   firewire: core: f...
947
948
949
950
951
952
953
954
955
956
957
958
  	if (!client->buffer_is_mapped) {
  		ret = fw_iso_buffer_map_dma(&client->buffer,
  					    client->device->card,
  					    iso_dma_direction(context));
  		if (ret < 0) {
  			spin_unlock_irq(&client->lock);
  			fw_iso_context_destroy(context);
  
  			return ret;
  		}
  		client->buffer_is_mapped = true;
  	}
6e95dea72   Stefan Richter   firewire: core: c...
959
  	client->iso_closure = a->closure;
24315c5e6   Kristian Høgsberg   firewire: Only se...
960
  	client->iso_context = context;
bdfe273ee   Clemens Ladisch   firewire: cdev: f...
961
  	spin_unlock_irq(&client->lock);
19a15b937   Kristian Høgsberg   firewire: Add dev...
962

6e95dea72   Stefan Richter   firewire: core: c...
963
  	a->handle = 0;
abaa5743e   Kristian Høgsberg   firewire: Future ...
964

19a15b937   Kristian Høgsberg   firewire: Add dev...
965
966
  	return 0;
  }
872e330e3   Stefan Richter   firewire: add iso...
967
968
969
970
971
972
973
974
975
976
  static int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg)
  {
  	struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels;
  	struct fw_iso_context *ctx = client->iso_context;
  
  	if (ctx == NULL || a->handle != 0)
  		return -EINVAL;
  
  	return fw_iso_context_set_channels(ctx, &a->channels);
  }
1ca31ae7c   Kristian Høgsberg   firewire: Change ...
977
978
979
980
  /* Macros for decoding the iso packet control header. */
  #define GET_PAYLOAD_LENGTH(v)	((v) & 0xffff)
  #define GET_INTERRUPT(v)	(((v) >> 16) & 0x01)
  #define GET_SKIP(v)		(((v) >> 17) & 0x01)
7a1003449   Stefan Richter   firewire: fix set...
981
982
  #define GET_TAG(v)		(((v) >> 18) & 0x03)
  #define GET_SY(v)		(((v) >> 20) & 0x0f)
1ca31ae7c   Kristian Høgsberg   firewire: Change ...
983
  #define GET_HEADER_LENGTH(v)	(((v) >> 24) & 0xff)
6e95dea72   Stefan Richter   firewire: core: c...
984
  static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
19a15b937   Kristian Høgsberg   firewire: Add dev...
985
  {
6e95dea72   Stefan Richter   firewire: core: c...
986
  	struct fw_cdev_queue_iso *a = &arg->queue_iso;
19a15b937   Kristian Høgsberg   firewire: Add dev...
987
  	struct fw_cdev_iso_packet __user *p, *end, *next;
9b32d5f30   Kristian Høgsberg   firewire: Acummul...
988
  	struct fw_iso_context *ctx = client->iso_context;
872e330e3   Stefan Richter   firewire: add iso...
989
  	unsigned long payload, buffer_end, transmit_header_bytes = 0;
1ca31ae7c   Kristian Høgsberg   firewire: Change ...
990
  	u32 control;
19a15b937   Kristian Høgsberg   firewire: Add dev...
991
992
993
994
995
  	int count;
  	struct {
  		struct fw_iso_packet packet;
  		u8 header[256];
  	} u;
6e95dea72   Stefan Richter   firewire: core: c...
996
  	if (ctx == NULL || a->handle != 0)
19a15b937   Kristian Høgsberg   firewire: Add dev...
997
  		return -EINVAL;
19a15b937   Kristian Høgsberg   firewire: Add dev...
998

c781c06d1   Kristian Høgsberg   firewire: Clean u...
999
1000
  	/*
  	 * If the user passes a non-NULL data pointer, has mmap()'ed
19a15b937   Kristian Høgsberg   firewire: Add dev...
1001
1002
  	 * the iso buffer, and the pointer points inside the buffer,
  	 * we setup the payload pointers accordingly.  Otherwise we
9aad81253   Kristian Høgsberg   firewire: Split t...
1003
  	 * set them both to 0, which will still let packets with
19a15b937   Kristian Høgsberg   firewire: Add dev...
1004
1005
  	 * payload_length == 0 through.  In other words, if no packets
  	 * use the indirect payload, the iso buffer need not be mapped
6e95dea72   Stefan Richter   firewire: core: c...
1006
  	 * and the a->data pointer is ignored.
c781c06d1   Kristian Høgsberg   firewire: Clean u...
1007
  	 */
6e95dea72   Stefan Richter   firewire: core: c...
1008
  	payload = (unsigned long)a->data - client->vm_start;
ef370ee74   Kristian Høgsberg   firewire: Fix the...
1009
  	buffer_end = client->buffer.page_count << PAGE_SHIFT;
6e95dea72   Stefan Richter   firewire: core: c...
1010
  	if (a->data == 0 || client->buffer.pages == NULL ||
ef370ee74   Kristian Høgsberg   firewire: Fix the...
1011
  	    payload >= buffer_end) {
9aad81253   Kristian Høgsberg   firewire: Split t...
1012
  		payload = 0;
ef370ee74   Kristian Høgsberg   firewire: Fix the...
1013
  		buffer_end = 0;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1014
  	}
872e330e3   Stefan Richter   firewire: add iso...
1015
1016
  	if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3)
  		return -EINVAL;
1ccc9147f   Al Viro   fw-cdev __user an...
1017

872e330e3   Stefan Richter   firewire: add iso...
1018
  	p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets);
6e95dea72   Stefan Richter   firewire: core: c...
1019
  	if (!access_ok(VERIFY_READ, p, a->size))
19a15b937   Kristian Høgsberg   firewire: Add dev...
1020
  		return -EFAULT;
6e95dea72   Stefan Richter   firewire: core: c...
1021
  	end = (void __user *)p + a->size;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1022
1023
  	count = 0;
  	while (p < end) {
1ca31ae7c   Kristian Høgsberg   firewire: Change ...
1024
  		if (get_user(control, &p->control))
19a15b937   Kristian Høgsberg   firewire: Add dev...
1025
  			return -EFAULT;
1ca31ae7c   Kristian Høgsberg   firewire: Change ...
1026
1027
1028
1029
1030
1031
  		u.packet.payload_length = GET_PAYLOAD_LENGTH(control);
  		u.packet.interrupt = GET_INTERRUPT(control);
  		u.packet.skip = GET_SKIP(control);
  		u.packet.tag = GET_TAG(control);
  		u.packet.sy = GET_SY(control);
  		u.packet.header_length = GET_HEADER_LENGTH(control);
295e3feb9   Kristian Høgsberg   firewire: Impleme...
1032

872e330e3   Stefan Richter   firewire: add iso...
1033
1034
1035
  		switch (ctx->type) {
  		case FW_ISO_CONTEXT_TRANSMIT:
  			if (u.packet.header_length & 3)
385ab5bcd   Clemens Ladisch   firewire: cdev: r...
1036
  				return -EINVAL;
ae2a97661   Stefan Richter   firewire: core: s...
1037
  			transmit_header_bytes = u.packet.header_length;
872e330e3   Stefan Richter   firewire: add iso...
1038
1039
1040
  			break;
  
  		case FW_ISO_CONTEXT_RECEIVE:
69e61d0c0   Stefan Richter   firewire: core: r...
1041
1042
  			if (u.packet.header_length == 0 ||
  			    u.packet.header_length % ctx->header_size != 0)
385ab5bcd   Clemens Ladisch   firewire: cdev: r...
1043
  				return -EINVAL;
872e330e3   Stefan Richter   firewire: add iso...
1044
1045
1046
1047
1048
  			break;
  
  		case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
  			if (u.packet.payload_length == 0 ||
  			    u.packet.payload_length & 3)
295e3feb9   Kristian Høgsberg   firewire: Impleme...
1049
  				return -EINVAL;
872e330e3   Stefan Richter   firewire: add iso...
1050
  			break;
295e3feb9   Kristian Høgsberg   firewire: Impleme...
1051
  		}
19a15b937   Kristian Høgsberg   firewire: Add dev...
1052
  		next = (struct fw_cdev_iso_packet __user *)
ae2a97661   Stefan Richter   firewire: core: s...
1053
  			&p->header[transmit_header_bytes / 4];
19a15b937   Kristian Høgsberg   firewire: Add dev...
1054
1055
1056
  		if (next > end)
  			return -EINVAL;
  		if (__copy_from_user
ae2a97661   Stefan Richter   firewire: core: s...
1057
  		    (u.packet.header, p->header, transmit_header_bytes))
19a15b937   Kristian Høgsberg   firewire: Add dev...
1058
  			return -EFAULT;
98b6cbe83   Kristian Høgsberg   firewire: Impleme...
1059
  		if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
19a15b937   Kristian Høgsberg   firewire: Add dev...
1060
1061
  		    u.packet.header_length + u.packet.payload_length > 0)
  			return -EINVAL;
ef370ee74   Kristian Høgsberg   firewire: Fix the...
1062
  		if (payload + u.packet.payload_length > buffer_end)
19a15b937   Kristian Høgsberg   firewire: Add dev...
1063
  			return -EINVAL;
9b32d5f30   Kristian Høgsberg   firewire: Acummul...
1064
1065
  		if (fw_iso_context_queue(ctx, &u.packet,
  					 &client->buffer, payload))
19a15b937   Kristian Høgsberg   firewire: Add dev...
1066
1067
1068
1069
1070
1071
  			break;
  
  		p = next;
  		payload += u.packet.payload_length;
  		count++;
  	}
13882a82e   Clemens Ladisch   firewire: optimiz...
1072
  	fw_iso_context_queue_flush(ctx);
19a15b937   Kristian Høgsberg   firewire: Add dev...
1073

6e95dea72   Stefan Richter   firewire: core: c...
1074
1075
1076
  	a->size    -= uptr_to_u64(p) - a->packets;
  	a->packets  = uptr_to_u64(p);
  	a->data     = client->vm_start + payload;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1077
1078
1079
  
  	return count;
  }
6e95dea72   Stefan Richter   firewire: core: c...
1080
  static int ioctl_start_iso(struct client *client, union ioctl_arg *arg)
19a15b937   Kristian Høgsberg   firewire: Add dev...
1081
  {
6e95dea72   Stefan Richter   firewire: core: c...
1082
  	struct fw_cdev_start_iso *a = &arg->start_iso;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1083

eb5b35a56   Stefan Richter   firewire: core: e...
1084
1085
1086
1087
1088
1089
  	BUILD_BUG_ON(
  	    FW_CDEV_ISO_CONTEXT_MATCH_TAG0 != FW_ISO_CONTEXT_MATCH_TAG0 ||
  	    FW_CDEV_ISO_CONTEXT_MATCH_TAG1 != FW_ISO_CONTEXT_MATCH_TAG1 ||
  	    FW_CDEV_ISO_CONTEXT_MATCH_TAG2 != FW_ISO_CONTEXT_MATCH_TAG2 ||
  	    FW_CDEV_ISO_CONTEXT_MATCH_TAG3 != FW_ISO_CONTEXT_MATCH_TAG3 ||
  	    FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS != FW_ISO_CONTEXT_MATCH_ALL_TAGS);
6e95dea72   Stefan Richter   firewire: core: c...
1090
  	if (client->iso_context == NULL || a->handle != 0)
abaa5743e   Kristian Høgsberg   firewire: Future ...
1091
  		return -EINVAL;
fae603121   Stefan Richter   firewire: fix NUL...
1092

6e95dea72   Stefan Richter   firewire: core: c...
1093
1094
1095
  	if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE &&
  	    (a->tags == 0 || a->tags > 15 || a->sync > 15))
  		return -EINVAL;
eb0306eac   Kristian Høgsberg   firewire: Move sy...
1096

6e95dea72   Stefan Richter   firewire: core: c...
1097
1098
  	return fw_iso_context_start(client->iso_context,
  				    a->cycle, a->sync, a->tags);
19a15b937   Kristian Høgsberg   firewire: Add dev...
1099
  }
6e95dea72   Stefan Richter   firewire: core: c...
1100
  static int ioctl_stop_iso(struct client *client, union ioctl_arg *arg)
b82956685   Kristian Høgsberg   firewire: Impleme...
1101
  {
6e95dea72   Stefan Richter   firewire: core: c...
1102
  	struct fw_cdev_stop_iso *a = &arg->stop_iso;
abaa5743e   Kristian Høgsberg   firewire: Future ...
1103

6e95dea72   Stefan Richter   firewire: core: c...
1104
  	if (client->iso_context == NULL || a->handle != 0)
abaa5743e   Kristian Høgsberg   firewire: Future ...
1105
  		return -EINVAL;
b82956685   Kristian Høgsberg   firewire: Impleme...
1106
1107
  	return fw_iso_context_stop(client->iso_context);
  }
d1bbd2097   Clemens Ladisch   firewire: allow e...
1108
1109
1110
1111
1112
1113
1114
1115
1116
  static int ioctl_flush_iso(struct client *client, union ioctl_arg *arg)
  {
  	struct fw_cdev_flush_iso *a = &arg->flush_iso;
  
  	if (client->iso_context == NULL || a->handle != 0)
  		return -EINVAL;
  
  	return fw_iso_context_flush_completions(client->iso_context);
  }
6e95dea72   Stefan Richter   firewire: core: c...
1117
  static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg)
a64408b96   Stefan Richter   firewire: adopt r...
1118
  {
6e95dea72   Stefan Richter   firewire: core: c...
1119
  	struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2;
a64408b96   Stefan Richter   firewire: adopt r...
1120
  	struct fw_card *card = client->device->card;
abfe5a01e   Stefan Richter   firewire: cdev: a...
1121
  	struct timespec ts = {0, 0};
4a9bde9b8   Stefan Richter   firewire: get_cyc...
1122
  	u32 cycle_time;
abfe5a01e   Stefan Richter   firewire: cdev: a...
1123
  	int ret = 0;
a64408b96   Stefan Richter   firewire: adopt r...
1124

4a9bde9b8   Stefan Richter   firewire: get_cyc...
1125
  	local_irq_disable();
a64408b96   Stefan Richter   firewire: adopt r...
1126

0fcff4e39   Stefan Richter   firewire: rename ...
1127
  	cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME);
abfe5a01e   Stefan Richter   firewire: cdev: a...
1128

6e95dea72   Stefan Richter   firewire: core: c...
1129
  	switch (a->clk_id) {
abfe5a01e   Stefan Richter   firewire: cdev: a...
1130
1131
1132
1133
1134
1135
  	case CLOCK_REALTIME:      getnstimeofday(&ts);                   break;
  	case CLOCK_MONOTONIC:     do_posix_clock_monotonic_gettime(&ts); break;
  	case CLOCK_MONOTONIC_RAW: getrawmonotonic(&ts);                  break;
  	default:
  		ret = -EINVAL;
  	}
a64408b96   Stefan Richter   firewire: adopt r...
1136

4a9bde9b8   Stefan Richter   firewire: get_cyc...
1137
  	local_irq_enable();
a64408b96   Stefan Richter   firewire: adopt r...
1138

6e95dea72   Stefan Richter   firewire: core: c...
1139
1140
1141
  	a->tv_sec      = ts.tv_sec;
  	a->tv_nsec     = ts.tv_nsec;
  	a->cycle_timer = cycle_time;
abfe5a01e   Stefan Richter   firewire: cdev: a...
1142
1143
1144
  
  	return ret;
  }
6e95dea72   Stefan Richter   firewire: core: c...
1145
  static int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg)
abfe5a01e   Stefan Richter   firewire: cdev: a...
1146
  {
6e95dea72   Stefan Richter   firewire: core: c...
1147
  	struct fw_cdev_get_cycle_timer *a = &arg->get_cycle_timer;
abfe5a01e   Stefan Richter   firewire: cdev: a...
1148
1149
1150
  	struct fw_cdev_get_cycle_timer2 ct2;
  
  	ct2.clk_id = CLOCK_REALTIME;
6e95dea72   Stefan Richter   firewire: core: c...
1151
  	ioctl_get_cycle_timer2(client, (union ioctl_arg *)&ct2);
abfe5a01e   Stefan Richter   firewire: cdev: a...
1152

6e95dea72   Stefan Richter   firewire: core: c...
1153
1154
  	a->local_time = ct2.tv_sec * USEC_PER_SEC + ct2.tv_nsec / NSEC_PER_USEC;
  	a->cycle_timer = ct2.cycle_timer;
4a9bde9b8   Stefan Richter   firewire: get_cyc...
1155

a64408b96   Stefan Richter   firewire: adopt r...
1156
1157
  	return 0;
  }
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
  static void iso_resource_work(struct work_struct *work)
  {
  	struct iso_resource_event *e;
  	struct iso_resource *r =
  			container_of(work, struct iso_resource, work.work);
  	struct client *client = r->client;
  	int generation, channel, bandwidth, todo;
  	bool skip, free, success;
  
  	spin_lock_irq(&client->lock);
  	generation = client->device->generation;
  	todo = r->todo;
  	/* Allow 1000ms grace period for other reallocations. */
  	if (todo == ISO_RES_ALLOC &&
e71084af5   Clemens Ladisch   firewire: core: f...
1172
1173
  	    time_before64(get_jiffies_64(),
  			  client->device->card->reset_jiffies + HZ)) {
9fb551bf7   Stefan Richter   firewire: normali...
1174
  		schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3));
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1175
1176
1177
1178
1179
1180
  		skip = true;
  	} else {
  		/* We could be called twice within the same generation. */
  		skip = todo == ISO_RES_REALLOC &&
  		       r->generation == generation;
  	}
1ec3c0269   Stefan Richter   firewire: cdev: a...
1181
1182
1183
  	free = todo == ISO_RES_DEALLOC ||
  	       todo == ISO_RES_ALLOC_ONCE ||
  	       todo == ISO_RES_DEALLOC_ONCE;
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
  	r->generation = generation;
  	spin_unlock_irq(&client->lock);
  
  	if (skip)
  		goto out;
  
  	bandwidth = r->bandwidth;
  
  	fw_iso_resource_manage(client->device->card, generation,
  			r->channels, &channel, &bandwidth,
1ec3c0269   Stefan Richter   firewire: cdev: a...
1194
1195
  			todo == ISO_RES_ALLOC ||
  			todo == ISO_RES_REALLOC ||
f30e6d3e4   Stefan Richter   firewire: octlet ...
1196
  			todo == ISO_RES_ALLOC_ONCE);
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
  	/*
  	 * Is this generation outdated already?  As long as this resource sticks
  	 * in the idr, it will be scheduled again for a newer generation or at
  	 * shutdown.
  	 */
  	if (channel == -EAGAIN &&
  	    (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC))
  		goto out;
  
  	success = channel >= 0 || bandwidth > 0;
  
  	spin_lock_irq(&client->lock);
  	/*
  	 * Transit from allocation to reallocation, except if the client
  	 * requested deallocation in the meantime.
  	 */
  	if (r->todo == ISO_RES_ALLOC)
  		r->todo = ISO_RES_REALLOC;
  	/*
  	 * Allocation or reallocation failure?  Pull this resource out of the
  	 * idr and prepare for deletion, unless the client is shutting down.
  	 */
  	if (r->todo == ISO_RES_REALLOC && !success &&
  	    !client->in_shutdown &&
  	    idr_find(&client->resource_idr, r->resource.handle)) {
  		idr_remove(&client->resource_idr, r->resource.handle);
  		client_put(client);
  		free = true;
  	}
  	spin_unlock_irq(&client->lock);
  
  	if (todo == ISO_RES_ALLOC && channel >= 0)
5d9cb7d27   Stefan Richter   firewire: cdev: a...
1229
  		r->channels = 1ULL << channel;
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1230
1231
1232
  
  	if (todo == ISO_RES_REALLOC && success)
  		goto out;
1ec3c0269   Stefan Richter   firewire: cdev: a...
1233
  	if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1234
1235
1236
1237
1238
1239
  		e = r->e_alloc;
  		r->e_alloc = NULL;
  	} else {
  		e = r->e_dealloc;
  		r->e_dealloc = NULL;
  	}
e21fcf798   Stefan Richter   firewire: cdev: n...
1240
1241
1242
  	e->iso_resource.handle    = r->resource.handle;
  	e->iso_resource.channel   = channel;
  	e->iso_resource.bandwidth = bandwidth;
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1243
1244
  
  	queue_event(client, &e->event,
e21fcf798   Stefan Richter   firewire: cdev: n...
1245
  		    &e->iso_resource, sizeof(e->iso_resource), NULL, 0);
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
  
  	if (free) {
  		cancel_delayed_work(&r->work);
  		kfree(r->e_alloc);
  		kfree(r->e_dealloc);
  		kfree(r);
  	}
   out:
  	client_put(client);
  }
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1256
1257
1258
1259
1260
1261
1262
1263
  static void release_iso_resource(struct client *client,
  				 struct client_resource *resource)
  {
  	struct iso_resource *r =
  		container_of(resource, struct iso_resource, resource);
  
  	spin_lock_irq(&client->lock);
  	r->todo = ISO_RES_DEALLOC;
9fb551bf7   Stefan Richter   firewire: normali...
1264
  	schedule_iso_resource(r, 0);
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1265
1266
  	spin_unlock_irq(&client->lock);
  }
1ec3c0269   Stefan Richter   firewire: cdev: a...
1267
1268
  static int init_iso_resource(struct client *client,
  		struct fw_cdev_allocate_iso_resource *request, int todo)
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1269
  {
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1270
1271
1272
1273
1274
  	struct iso_resource_event *e1, *e2;
  	struct iso_resource *r;
  	int ret;
  
  	if ((request->channels == 0 && request->bandwidth == 0) ||
bdabfa546   Stefan Richter   firewire: core: r...
1275
  	    request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL)
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
  		return -EINVAL;
  
  	r  = kmalloc(sizeof(*r), GFP_KERNEL);
  	e1 = kmalloc(sizeof(*e1), GFP_KERNEL);
  	e2 = kmalloc(sizeof(*e2), GFP_KERNEL);
  	if (r == NULL || e1 == NULL || e2 == NULL) {
  		ret = -ENOMEM;
  		goto fail;
  	}
  
  	INIT_DELAYED_WORK(&r->work, iso_resource_work);
  	r->client	= client;
1ec3c0269   Stefan Richter   firewire: cdev: a...
1288
  	r->todo		= todo;
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1289
1290
1291
1292
1293
  	r->generation	= -1;
  	r->channels	= request->channels;
  	r->bandwidth	= request->bandwidth;
  	r->e_alloc	= e1;
  	r->e_dealloc	= e2;
e21fcf798   Stefan Richter   firewire: cdev: n...
1294
1295
1296
1297
  	e1->iso_resource.closure = request->closure;
  	e1->iso_resource.type    = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED;
  	e2->iso_resource.closure = request->closure;
  	e2->iso_resource.type    = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1298

1ec3c0269   Stefan Richter   firewire: cdev: a...
1299
1300
1301
  	if (todo == ISO_RES_ALLOC) {
  		r->resource.release = release_iso_resource;
  		ret = add_client_resource(client, &r->resource, GFP_KERNEL);
81610b8fb   Stefan Richter   firewire: cdev: s...
1302
1303
  		if (ret < 0)
  			goto fail;
1ec3c0269   Stefan Richter   firewire: cdev: a...
1304
1305
1306
  	} else {
  		r->resource.release = NULL;
  		r->resource.handle = -1;
9fb551bf7   Stefan Richter   firewire: normali...
1307
  		schedule_iso_resource(r, 0);
1ec3c0269   Stefan Richter   firewire: cdev: a...
1308
  	}
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
  	request->handle = r->resource.handle;
  
  	return 0;
   fail:
  	kfree(r);
  	kfree(e1);
  	kfree(e2);
  
  	return ret;
  }
6e95dea72   Stefan Richter   firewire: core: c...
1319
1320
  static int ioctl_allocate_iso_resource(struct client *client,
  				       union ioctl_arg *arg)
1ec3c0269   Stefan Richter   firewire: cdev: a...
1321
  {
6e95dea72   Stefan Richter   firewire: core: c...
1322
1323
  	return init_iso_resource(client,
  			&arg->allocate_iso_resource, ISO_RES_ALLOC);
1ec3c0269   Stefan Richter   firewire: cdev: a...
1324
  }
6e95dea72   Stefan Richter   firewire: core: c...
1325
1326
  static int ioctl_deallocate_iso_resource(struct client *client,
  					 union ioctl_arg *arg)
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1327
  {
6e95dea72   Stefan Richter   firewire: core: c...
1328
1329
  	return release_client_resource(client,
  			arg->deallocate.handle, release_iso_resource, NULL);
b1bda4cdc   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1330
  }
6e95dea72   Stefan Richter   firewire: core: c...
1331
1332
  static int ioctl_allocate_iso_resource_once(struct client *client,
  					    union ioctl_arg *arg)
1ec3c0269   Stefan Richter   firewire: cdev: a...
1333
  {
6e95dea72   Stefan Richter   firewire: core: c...
1334
1335
  	return init_iso_resource(client,
  			&arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE);
1ec3c0269   Stefan Richter   firewire: cdev: a...
1336
  }
6e95dea72   Stefan Richter   firewire: core: c...
1337
1338
  static int ioctl_deallocate_iso_resource_once(struct client *client,
  					      union ioctl_arg *arg)
1ec3c0269   Stefan Richter   firewire: cdev: a...
1339
  {
6e95dea72   Stefan Richter   firewire: core: c...
1340
1341
  	return init_iso_resource(client,
  			&arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE);
1ec3c0269   Stefan Richter   firewire: cdev: a...
1342
  }
c8a25900f   Stefan Richter   firewire: cdev: a...
1343
1344
1345
1346
1347
  /*
   * Returns a speed code:  Maximum speed to or from this device,
   * limited by the device's link speed, the local node's link speed,
   * and all PHY port speeds between the two links.
   */
6e95dea72   Stefan Richter   firewire: core: c...
1348
  static int ioctl_get_speed(struct client *client, union ioctl_arg *arg)
33580a3ef   Stefan Richter   firewire: cdev: a...
1349
  {
c8a25900f   Stefan Richter   firewire: cdev: a...
1350
  	return client->device->max_speed;
33580a3ef   Stefan Richter   firewire: cdev: a...
1351
  }
6e95dea72   Stefan Richter   firewire: core: c...
1352
1353
  static int ioctl_send_broadcast_request(struct client *client,
  					union ioctl_arg *arg)
acfe83335   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1354
  {
6e95dea72   Stefan Richter   firewire: core: c...
1355
  	struct fw_cdev_send_request *a = &arg->send_request;
acfe83335   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1356

6e95dea72   Stefan Richter   firewire: core: c...
1357
  	switch (a->tcode) {
acfe83335   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1358
1359
1360
1361
1362
1363
  	case TCODE_WRITE_QUADLET_REQUEST:
  	case TCODE_WRITE_BLOCK_REQUEST:
  		break;
  	default:
  		return -EINVAL;
  	}
1566f3dc3   Stefan Richter   firewire: cdev: r...
1364
  	/* Security policy: Only allow accesses to Units Space. */
6e95dea72   Stefan Richter   firewire: core: c...
1365
  	if (a->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END)
1566f3dc3   Stefan Richter   firewire: cdev: r...
1366
  		return -EACCES;
6e95dea72   Stefan Richter   firewire: core: c...
1367
  	return init_request(client, a, LOCAL_BUS | 0x3f, SCODE_100);
acfe83335   Jay Fenlason, Stefan Richter   firewire: cdev: a...
1368
  }
6e95dea72   Stefan Richter   firewire: core: c...
1369
  static int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg)
f8c2287c6   Jay Fenlason   firewire: impleme...
1370
  {
6e95dea72   Stefan Richter   firewire: core: c...
1371
  	struct fw_cdev_send_stream_packet *a = &arg->send_stream_packet;
18e9b10fc   Stefan Richter   firewire: cdev: a...
1372
1373
  	struct fw_cdev_send_request request;
  	int dest;
f8c2287c6   Jay Fenlason   firewire: impleme...
1374

6e95dea72   Stefan Richter   firewire: core: c...
1375
1376
  	if (a->speed > client->device->card->link_speed ||
  	    a->length > 1024 << a->speed)
18e9b10fc   Stefan Richter   firewire: cdev: a...
1377
  		return -EIO;
f8c2287c6   Jay Fenlason   firewire: impleme...
1378

6e95dea72   Stefan Richter   firewire: core: c...
1379
  	if (a->tag > 3 || a->channel > 63 || a->sy > 15)
18e9b10fc   Stefan Richter   firewire: cdev: a...
1380
  		return -EINVAL;
6e95dea72   Stefan Richter   firewire: core: c...
1381
  	dest = fw_stream_packet_destination_id(a->tag, a->channel, a->sy);
18e9b10fc   Stefan Richter   firewire: cdev: a...
1382
  	request.tcode		= TCODE_STREAM_DATA;
6e95dea72   Stefan Richter   firewire: core: c...
1383
1384
1385
1386
  	request.length		= a->length;
  	request.closure		= a->closure;
  	request.data		= a->data;
  	request.generation	= a->generation;
18e9b10fc   Stefan Richter   firewire: cdev: a...
1387

6e95dea72   Stefan Richter   firewire: core: c...
1388
  	return init_request(client, &request, dest, a->speed);
f8c2287c6   Jay Fenlason   firewire: impleme...
1389
  }
850bb6f23   Stefan Richter   firewire: cdev: a...
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
  static void outbound_phy_packet_callback(struct fw_packet *packet,
  					 struct fw_card *card, int status)
  {
  	struct outbound_phy_packet_event *e =
  		container_of(packet, struct outbound_phy_packet_event, p);
  
  	switch (status) {
  	/* expected: */
  	case ACK_COMPLETE:	e->phy_packet.rcode = RCODE_COMPLETE;	break;
  	/* should never happen with PHY packets: */
  	case ACK_PENDING:	e->phy_packet.rcode = RCODE_COMPLETE;	break;
  	case ACK_BUSY_X:
  	case ACK_BUSY_A:
  	case ACK_BUSY_B:	e->phy_packet.rcode = RCODE_BUSY;	break;
  	case ACK_DATA_ERROR:	e->phy_packet.rcode = RCODE_DATA_ERROR;	break;
  	case ACK_TYPE_ERROR:	e->phy_packet.rcode = RCODE_TYPE_ERROR;	break;
  	/* stale generation; cancelled; on certain controllers: no ack */
  	default:		e->phy_packet.rcode = status;		break;
  	}
cc550216a   Stefan Richter   firewire: cdev: a...
1409
  	e->phy_packet.data[0] = packet->timestamp;
850bb6f23   Stefan Richter   firewire: cdev: a...
1410

cc550216a   Stefan Richter   firewire: cdev: a...
1411
1412
  	queue_event(e->client, &e->event, &e->phy_packet,
  		    sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0);
850bb6f23   Stefan Richter   firewire: cdev: a...
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
  	client_put(e->client);
  }
  
  static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)
  {
  	struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet;
  	struct fw_card *card = client->device->card;
  	struct outbound_phy_packet_event *e;
  
  	/* Access policy: Allow this ioctl only on local nodes' device files. */
  	if (!client->device->is_local)
  		return -ENOSYS;
cc550216a   Stefan Richter   firewire: cdev: a...
1425
  	e = kzalloc(sizeof(*e) + 4, GFP_KERNEL);
850bb6f23   Stefan Richter   firewire: cdev: a...
1426
1427
1428
1429
1430
1431
1432
  	if (e == NULL)
  		return -ENOMEM;
  
  	client_get(client);
  	e->client		= client;
  	e->p.speed		= SCODE_100;
  	e->p.generation		= a->generation;
5b06db166   Clemens Ladisch   firewire: make PH...
1433
1434
1435
1436
  	e->p.header[0]		= TCODE_LINK_INTERNAL << 4;
  	e->p.header[1]		= a->data[0];
  	e->p.header[2]		= a->data[1];
  	e->p.header_length	= 12;
850bb6f23   Stefan Richter   firewire: cdev: a...
1437
1438
1439
  	e->p.callback		= outbound_phy_packet_callback;
  	e->phy_packet.closure	= a->closure;
  	e->phy_packet.type	= FW_CDEV_EVENT_PHY_PACKET_SENT;
cc550216a   Stefan Richter   firewire: cdev: a...
1440
1441
  	if (is_ping_packet(a->data))
  			e->phy_packet.length = 4;
850bb6f23   Stefan Richter   firewire: cdev: a...
1442
1443
1444
1445
1446
  
  	card->driver->send_request(card, &e->p);
  
  	return 0;
  }
bf54e1462   Stefan Richter   firewire: cdev: a...
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
  static int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg)
  {
  	struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets;
  	struct fw_card *card = client->device->card;
  
  	/* Access policy: Allow this ioctl only on local nodes' device files. */
  	if (!client->device->is_local)
  		return -ENOSYS;
  
  	spin_lock_irq(&card->lock);
  
  	list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list);
  	client->phy_receiver_closure = a->closure;
  
  	spin_unlock_irq(&card->lock);
  
  	return 0;
  }
  
  void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p)
  {
  	struct client *client;
  	struct inbound_phy_packet_event *e;
  	unsigned long flags;
  
  	spin_lock_irqsave(&card->lock, flags);
  
  	list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) {
  		e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC);
cfb0c9d1f   Stefan Richter   firewire: remove ...
1476
  		if (e == NULL)
bf54e1462   Stefan Richter   firewire: cdev: a...
1477
  			break;
cfb0c9d1f   Stefan Richter   firewire: remove ...
1478

bf54e1462   Stefan Richter   firewire: cdev: a...
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
  		e->phy_packet.closure	= client->phy_receiver_closure;
  		e->phy_packet.type	= FW_CDEV_EVENT_PHY_PACKET_RECEIVED;
  		e->phy_packet.rcode	= RCODE_COMPLETE;
  		e->phy_packet.length	= 8;
  		e->phy_packet.data[0]	= p->header[1];
  		e->phy_packet.data[1]	= p->header[2];
  		queue_event(client, &e->event,
  			    &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0);
  	}
  
  	spin_unlock_irqrestore(&card->lock, flags);
  }
6e95dea72   Stefan Richter   firewire: core: c...
1491
  static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
b9dc61cf4   Stefan Richter   firewire: core: u...
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
  	[0x00] = ioctl_get_info,
  	[0x01] = ioctl_send_request,
  	[0x02] = ioctl_allocate,
  	[0x03] = ioctl_deallocate,
  	[0x04] = ioctl_send_response,
  	[0x05] = ioctl_initiate_bus_reset,
  	[0x06] = ioctl_add_descriptor,
  	[0x07] = ioctl_remove_descriptor,
  	[0x08] = ioctl_create_iso_context,
  	[0x09] = ioctl_queue_iso,
  	[0x0a] = ioctl_start_iso,
  	[0x0b] = ioctl_stop_iso,
  	[0x0c] = ioctl_get_cycle_timer,
  	[0x0d] = ioctl_allocate_iso_resource,
  	[0x0e] = ioctl_deallocate_iso_resource,
  	[0x0f] = ioctl_allocate_iso_resource_once,
  	[0x10] = ioctl_deallocate_iso_resource_once,
  	[0x11] = ioctl_get_speed,
  	[0x12] = ioctl_send_broadcast_request,
  	[0x13] = ioctl_send_stream_packet,
  	[0x14] = ioctl_get_cycle_timer2,
850bb6f23   Stefan Richter   firewire: cdev: a...
1513
  	[0x15] = ioctl_send_phy_packet,
bf54e1462   Stefan Richter   firewire: cdev: a...
1514
  	[0x16] = ioctl_receive_phy_packets,
872e330e3   Stefan Richter   firewire: add iso...
1515
  	[0x17] = ioctl_set_iso_channels,
d1bbd2097   Clemens Ladisch   firewire: allow e...
1516
  	[0x18] = ioctl_flush_iso,
4f2592232   Kristian Høgsberg   firewire: Add rea...
1517
  };
53dca5117   Stefan Richter   firewire: remove ...
1518
1519
  static int dispatch_ioctl(struct client *client,
  			  unsigned int cmd, void __user *arg)
19a15b937   Kristian Høgsberg   firewire: Add dev...
1520
  {
6e95dea72   Stefan Richter   firewire: core: c...
1521
  	union ioctl_arg buffer;
2dbd7d7e2   Stefan Richter   firewire: standar...
1522
  	int ret;
4f2592232   Kristian Høgsberg   firewire: Add rea...
1523

64582298b   Stefan Richter   firewire: core: c...
1524
1525
  	if (fw_device_is_shutdown(client->device))
  		return -ENODEV;
4f2592232   Kristian Høgsberg   firewire: Add rea...
1526
  	if (_IOC_TYPE(cmd) != '#' ||
9cac00b8f   Stefan Richter   firewire: cdev: f...
1527
1528
  	    _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) ||
  	    _IOC_SIZE(cmd) > sizeof(buffer))
d873d7942   Stefan Richter   firewire: cdev: r...
1529
  		return -ENOTTY;
4f2592232   Kristian Høgsberg   firewire: Add rea...
1530

9cac00b8f   Stefan Richter   firewire: cdev: f...
1531
1532
1533
1534
1535
  	if (_IOC_DIR(cmd) == _IOC_READ)
  		memset(&buffer, 0, _IOC_SIZE(cmd));
  
  	if (_IOC_DIR(cmd) & _IOC_WRITE)
  		if (copy_from_user(&buffer, arg, _IOC_SIZE(cmd)))
4f2592232   Kristian Høgsberg   firewire: Add rea...
1536
  			return -EFAULT;
4f2592232   Kristian Høgsberg   firewire: Add rea...
1537

6e95dea72   Stefan Richter   firewire: core: c...
1538
  	ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer);
2dbd7d7e2   Stefan Richter   firewire: standar...
1539
1540
  	if (ret < 0)
  		return ret;
4f2592232   Kristian Høgsberg   firewire: Add rea...
1541

9cac00b8f   Stefan Richter   firewire: cdev: f...
1542
1543
  	if (_IOC_DIR(cmd) & _IOC_READ)
  		if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd)))
4f2592232   Kristian Høgsberg   firewire: Add rea...
1544
  			return -EFAULT;
4f2592232   Kristian Høgsberg   firewire: Add rea...
1545

2dbd7d7e2   Stefan Richter   firewire: standar...
1546
  	return ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1547
  }
53dca5117   Stefan Richter   firewire: remove ...
1548
1549
  static long fw_device_op_ioctl(struct file *file,
  			       unsigned int cmd, unsigned long arg)
19a15b937   Kristian Høgsberg   firewire: Add dev...
1550
  {
64582298b   Stefan Richter   firewire: core: c...
1551
  	return dispatch_ioctl(file->private_data, cmd, (void __user *)arg);
19a15b937   Kristian Høgsberg   firewire: Add dev...
1552
1553
1554
  }
  
  #ifdef CONFIG_COMPAT
53dca5117   Stefan Richter   firewire: remove ...
1555
1556
  static long fw_device_op_compat_ioctl(struct file *file,
  				      unsigned int cmd, unsigned long arg)
19a15b937   Kristian Høgsberg   firewire: Add dev...
1557
  {
64582298b   Stefan Richter   firewire: core: c...
1558
  	return dispatch_ioctl(file->private_data, cmd, compat_ptr(arg));
19a15b937   Kristian Høgsberg   firewire: Add dev...
1559
1560
1561
1562
1563
1564
  }
  #endif
  
  static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
  {
  	struct client *client = file->private_data;
9aad81253   Kristian Høgsberg   firewire: Split t...
1565
  	unsigned long size;
2dbd7d7e2   Stefan Richter   firewire: standar...
1566
  	int page_count, ret;
9aad81253   Kristian Høgsberg   firewire: Split t...
1567

551f4cb9d   Jay Fenlason   firewire: prevent...
1568
1569
  	if (fw_device_is_shutdown(client->device))
  		return -ENODEV;
9aad81253   Kristian Høgsberg   firewire: Split t...
1570
1571
1572
1573
1574
1575
  	/* FIXME: We could support multiple buffers, but we don't. */
  	if (client->buffer.pages != NULL)
  		return -EBUSY;
  
  	if (!(vma->vm_flags & VM_SHARED))
  		return -EINVAL;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1576

9aad81253   Kristian Høgsberg   firewire: Split t...
1577
  	if (vma->vm_start & ~PAGE_MASK)
19a15b937   Kristian Høgsberg   firewire: Add dev...
1578
1579
1580
  		return -EINVAL;
  
  	client->vm_start = vma->vm_start;
9aad81253   Kristian Høgsberg   firewire: Split t...
1581
1582
1583
1584
  	size = vma->vm_end - vma->vm_start;
  	page_count = size >> PAGE_SHIFT;
  	if (size & ~PAGE_MASK)
  		return -EINVAL;
0b6c4857f   Stefan Richter   firewire: core: f...
1585
  	ret = fw_iso_buffer_alloc(&client->buffer, page_count);
2dbd7d7e2   Stefan Richter   firewire: standar...
1586
1587
  	if (ret < 0)
  		return ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1588

0b6c4857f   Stefan Richter   firewire: core: f...
1589
1590
1591
1592
1593
1594
1595
1596
  	spin_lock_irq(&client->lock);
  	if (client->iso_context) {
  		ret = fw_iso_buffer_map_dma(&client->buffer,
  				client->device->card,
  				iso_dma_direction(client->iso_context));
  		client->buffer_is_mapped = (ret == 0);
  	}
  	spin_unlock_irq(&client->lock);
2dbd7d7e2   Stefan Richter   firewire: standar...
1597
  	if (ret < 0)
0b6c4857f   Stefan Richter   firewire: core: f...
1598
  		goto fail;
9aad81253   Kristian Høgsberg   firewire: Split t...
1599

0b6c4857f   Stefan Richter   firewire: core: f...
1600
1601
1602
1603
1604
1605
1606
  	ret = fw_iso_buffer_map_vma(&client->buffer, vma);
  	if (ret < 0)
  		goto fail;
  
  	return 0;
   fail:
  	fw_iso_buffer_destroy(&client->buffer, client->device->card);
2dbd7d7e2   Stefan Richter   firewire: standar...
1607
  	return ret;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1608
  }
5a5e62da9   Clemens Ladisch   firewire: cdev: a...
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
  static int is_outbound_transaction_resource(int id, void *p, void *data)
  {
  	struct client_resource *resource = p;
  
  	return resource->release == release_transaction;
  }
  
  static int has_outbound_transactions(struct client *client)
  {
  	int ret;
  
  	spin_lock_irq(&client->lock);
  	ret = idr_for_each(&client->resource_idr,
  			   is_outbound_transaction_resource, NULL);
  	spin_unlock_irq(&client->lock);
  
  	return ret;
  }
45ee3199e   Jay Fenlason   firewire: cdev: u...
1627
1628
  static int shutdown_resource(int id, void *p, void *data)
  {
e21fcf798   Stefan Richter   firewire: cdev: n...
1629
  	struct client_resource *resource = p;
45ee3199e   Jay Fenlason   firewire: cdev: u...
1630
  	struct client *client = data;
e21fcf798   Stefan Richter   firewire: cdev: n...
1631
  	resource->release(client, resource);
fb4430367   Stefan Richter   firewire: cdev: r...
1632
  	client_put(client);
45ee3199e   Jay Fenlason   firewire: cdev: u...
1633
1634
1635
  
  	return 0;
  }
19a15b937   Kristian Høgsberg   firewire: Add dev...
1636
1637
1638
  static int fw_device_op_release(struct inode *inode, struct file *file)
  {
  	struct client *client = file->private_data;
e21fcf798   Stefan Richter   firewire: cdev: n...
1639
  	struct event *event, *next_event;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1640

bf54e1462   Stefan Richter   firewire: cdev: a...
1641
1642
1643
  	spin_lock_irq(&client->device->card->lock);
  	list_del(&client->phy_receiver_link);
  	spin_unlock_irq(&client->device->card->lock);
97811e347   Stefan Richter   firewire: cdev: f...
1644
1645
1646
  	mutex_lock(&client->device->client_list_mutex);
  	list_del(&client->link);
  	mutex_unlock(&client->device->client_list_mutex);
19a15b937   Kristian Høgsberg   firewire: Add dev...
1647
1648
  	if (client->iso_context)
  		fw_iso_context_destroy(client->iso_context);
36a755cfc   Stefan Richter   firewire: cdev: s...
1649
1650
  	if (client->buffer.pages)
  		fw_iso_buffer_destroy(&client->buffer, client->device->card);
45ee3199e   Jay Fenlason   firewire: cdev: u...
1651
  	/* Freeze client->resource_idr and client->event_list */
3ba949868   Stefan Richter   firewire: cdev: r...
1652
  	spin_lock_irq(&client->lock);
45ee3199e   Jay Fenlason   firewire: cdev: u...
1653
  	client->in_shutdown = true;
3ba949868   Stefan Richter   firewire: cdev: r...
1654
  	spin_unlock_irq(&client->lock);
66dea3e5f   Kristian Høgsberg   firewire: Add ioc...
1655

5a5e62da9   Clemens Ladisch   firewire: cdev: a...
1656
  	wait_event(client->tx_flush_wait, !has_outbound_transactions(client));
45ee3199e   Jay Fenlason   firewire: cdev: u...
1657
  	idr_for_each(&client->resource_idr, shutdown_resource, client);
45ee3199e   Jay Fenlason   firewire: cdev: u...
1658
  	idr_destroy(&client->resource_idr);
28cf6a04c   Kristian Høgsberg   firewire: Track p...
1659

e21fcf798   Stefan Richter   firewire: cdev: n...
1660
1661
  	list_for_each_entry_safe(event, next_event, &client->event_list, link)
  		kfree(event);
19a15b937   Kristian Høgsberg   firewire: Add dev...
1662

fb4430367   Stefan Richter   firewire: cdev: r...
1663
  	client_put(client);
19a15b937   Kristian Høgsberg   firewire: Add dev...
1664
1665
1666
1667
1668
1669
1670
  
  	return 0;
  }
  
  static unsigned int fw_device_op_poll(struct file *file, poll_table * pt)
  {
  	struct client *client = file->private_data;
2603bf219   Kristian Høgsberg   firewire: Use onl...
1671
  	unsigned int mask = 0;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1672
1673
  
  	poll_wait(file, &client->wait, pt);
2603bf219   Kristian Høgsberg   firewire: Use onl...
1674
1675
  	if (fw_device_is_shutdown(client->device))
  		mask |= POLLHUP | POLLERR;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1676
  	if (!list_empty(&client->event_list))
2603bf219   Kristian Høgsberg   firewire: Use onl...
1677
1678
1679
  		mask |= POLLIN | POLLRDNORM;
  
  	return mask;
19a15b937   Kristian Høgsberg   firewire: Add dev...
1680
  }
21ebcd122   Stefan Richter   firewire: mark so...
1681
  const struct file_operations fw_device_ops = {
19a15b937   Kristian Høgsberg   firewire: Add dev...
1682
  	.owner		= THIS_MODULE,
3ac26b2ee   Stefan Richter   firewire: cdev: m...
1683
  	.llseek		= no_llseek,
19a15b937   Kristian Høgsberg   firewire: Add dev...
1684
1685
1686
  	.open		= fw_device_op_open,
  	.read		= fw_device_op_read,
  	.unlocked_ioctl	= fw_device_op_ioctl,
19a15b937   Kristian Høgsberg   firewire: Add dev...
1687
  	.mmap		= fw_device_op_mmap,
3ac26b2ee   Stefan Richter   firewire: cdev: m...
1688
1689
  	.release	= fw_device_op_release,
  	.poll		= fw_device_op_poll,
19a15b937   Kristian Høgsberg   firewire: Add dev...
1690
  #ifdef CONFIG_COMPAT
5af4e5eab   Stefan Richter   firewire: comma a...
1691
  	.compat_ioctl	= fw_device_op_compat_ioctl,
19a15b937   Kristian Høgsberg   firewire: Add dev...
1692
1693
  #endif
  };