Blame view
drivers/firewire/core-cdev.c
46.1 KB
c781c06d1 firewire: Clean u... |
1 2 |
/* * Char device for device raw access |
19a15b937 firewire: Add dev... |
3 |
* |
c781c06d1 firewire: Clean u... |
4 |
* Copyright (C) 2005-2007 Kristian Hoegsberg <krh@bitplanet.net> |
19a15b937 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 firewire: core: e... |
20 |
#include <linux/bug.h> |
be5bbd675 firewire: cdev: s... |
21 22 23 |
#include <linux/compat.h> #include <linux/delay.h> #include <linux/device.h> |
0b6c4857f firewire: core: f... |
24 |
#include <linux/dma-mapping.h> |
be5bbd675 firewire: cdev: s... |
25 |
#include <linux/errno.h> |
77c9a5daa firewire: reorgan... |
26 |
#include <linux/firewire.h> |
be5bbd675 firewire: cdev: s... |
27 28 |
#include <linux/firewire-cdev.h> #include <linux/idr.h> |
4a9bde9b8 firewire: get_cyc... |
29 |
#include <linux/irqflags.h> |
b1bda4cdc firewire: cdev: a... |
30 |
#include <linux/jiffies.h> |
19a15b937 firewire: Add dev... |
31 |
#include <linux/kernel.h> |
fb4430367 firewire: cdev: r... |
32 |
#include <linux/kref.h> |
be5bbd675 firewire: cdev: s... |
33 34 |
#include <linux/mm.h> #include <linux/module.h> |
d67cfb961 firewire: convert... |
35 |
#include <linux/mutex.h> |
19a15b937 firewire: Add dev... |
36 |
#include <linux/poll.h> |
ae2a97661 firewire: core: s... |
37 |
#include <linux/sched.h> /* required for linux/wait.h */ |
5a0e3ad6a include cleanup: ... |
38 |
#include <linux/slab.h> |
cf417e549 firewire: add a c... |
39 |
#include <linux/spinlock.h> |
281e20323 firewire: core: f... |
40 |
#include <linux/string.h> |
be5bbd675 firewire: cdev: s... |
41 |
#include <linux/time.h> |
e034d2425 firewire: core: i... |
42 |
#include <linux/uaccess.h> |
be5bbd675 firewire: cdev: s... |
43 44 |
#include <linux/vmalloc.h> #include <linux/wait.h> |
b1bda4cdc firewire: cdev: a... |
45 |
#include <linux/workqueue.h> |
be5bbd675 firewire: cdev: s... |
46 |
|
be5bbd675 firewire: cdev: s... |
47 |
|
77c9a5daa firewire: reorgan... |
48 |
#include "core.h" |
19a15b937 firewire: Add dev... |
49 |
|
604f45167 firewire: cdev: f... |
50 51 52 |
/* * ABI version history is documented in linux/firewire-cdev.h. */ |
18d627113 firewire: prevent... |
53 |
#define FW_CDEV_KERNEL_VERSION 5 |
8e2b2b46e firewire: cdev: i... |
54 55 |
#define FW_CDEV_VERSION_EVENT_REQUEST2 4 #define FW_CDEV_VERSION_ALLOCATE_REGION_END 4 |
604f45167 firewire: cdev: f... |
56 |
|
19a15b937 firewire: Add dev... |
57 |
struct client { |
344bbc4de firewire: General... |
58 |
u32 version; |
19a15b937 firewire: Add dev... |
59 |
struct fw_device *device; |
45ee3199e firewire: cdev: u... |
60 |
|
19a15b937 firewire: Add dev... |
61 |
spinlock_t lock; |
45ee3199e firewire: cdev: u... |
62 63 |
bool in_shutdown; struct idr resource_idr; |
19a15b937 firewire: Add dev... |
64 |
struct list_head event_list; |
19a15b937 firewire: Add dev... |
65 |
wait_queue_head_t wait; |
5a5e62da9 firewire: cdev: a... |
66 |
wait_queue_head_t tx_flush_wait; |
da8ecffae firewire: Streaml... |
67 |
u64 bus_reset_closure; |
9aad81253 firewire: Split t... |
68 |
|
19a15b937 firewire: Add dev... |
69 |
struct fw_iso_context *iso_context; |
abaa5743e firewire: Future ... |
70 |
u64 iso_closure; |
9aad81253 firewire: Split t... |
71 72 |
struct fw_iso_buffer buffer; unsigned long vm_start; |
0b6c4857f firewire: core: f... |
73 |
bool buffer_is_mapped; |
97bd9efa5 firewire: Add a b... |
74 |
|
bf54e1462 firewire: cdev: a... |
75 76 |
struct list_head phy_receiver_link; u64 phy_receiver_closure; |
97bd9efa5 firewire: Add a b... |
77 |
struct list_head link; |
fb4430367 firewire: cdev: r... |
78 |
struct kref kref; |
19a15b937 firewire: Add dev... |
79 |
}; |
fb4430367 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 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 firewire: cdev: f... |
119 |
struct fw_card *card; |
97c18b7fd 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 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 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 firewire: cdev: a... |
137 138 139 140 141 |
int generation; u64 channels; s32 bandwidth; struct iso_resource_event *e_alloc, *e_dealloc; }; |
b1bda4cdc firewire: cdev: a... |
142 |
static void release_iso_resource(struct client *, struct client_resource *); |
9fb551bf7 firewire: normali... |
143 144 145 |
static void schedule_iso_resource(struct iso_resource *r, unsigned long delay) { client_get(r->client); |
105e53f86 firewire: sbp2: p... |
146 |
if (!queue_delayed_work(fw_workqueue, &r->work, delay)) |
9fb551bf7 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 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 firewire: cdev: f... |
179 180 181 182 |
union { struct fw_cdev_event_request request; struct fw_cdev_event_request2 request2; } req; |
97c18b7fd firewire: cdev: u... |
183 184 185 186 187 188 |
}; struct iso_interrupt_event { struct event event; struct fw_cdev_event_iso_interrupt interrupt; }; |
872e330e3 firewire: add iso... |
189 190 191 192 |
struct iso_interrupt_mc_event { struct event event; struct fw_cdev_event_iso_interrupt_mc interrupt; }; |
b1bda4cdc firewire: cdev: a... |
193 194 |
struct iso_resource_event { struct event event; |
e21fcf798 firewire: cdev: n... |
195 |
struct fw_cdev_event_iso_resource iso_resource; |
b1bda4cdc firewire: cdev: a... |
196 |
}; |
850bb6f23 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 firewire: cdev: a... |
203 204 205 206 |
struct inbound_phy_packet_event { struct event event; struct fw_cdev_event_phy_packet phy_packet; }; |
9c1176b6a 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 firewire: Add dev... |
225 226 227 |
{ return (void __user *)(unsigned long)value; } |
9c1176b6a firewire: cdev: f... |
228 |
static inline u64 uptr_to_u64(void __user *ptr) |
19a15b937 firewire: Add dev... |
229 |
{ |
9c1176b6a firewire: cdev: f... |
230 |
return (u64)(unsigned long)ptr; |
19a15b937 firewire: Add dev... |
231 |
} |
9c1176b6a firewire: cdev: f... |
232 |
#endif /* CONFIG_COMPAT */ |
19a15b937 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 firewire: fix "ko... |
238 |
device = fw_device_get_by_devt(inode->i_rdev); |
a3aca3dab firewire: Switch ... |
239 240 |
if (device == NULL) return -ENODEV; |
19a15b937 firewire: Add dev... |
241 |
|
551f4cb9d firewire: prevent... |
242 243 244 245 |
if (fw_device_is_shutdown(device)) { fw_device_put(device); return -ENODEV; } |
2d826cc5c firewire: Always ... |
246 |
client = kzalloc(sizeof(*client), GFP_KERNEL); |
96b19062e firewire: fix "ko... |
247 248 |
if (client == NULL) { fw_device_put(device); |
19a15b937 firewire: Add dev... |
249 |
return -ENOMEM; |
96b19062e firewire: fix "ko... |
250 |
} |
19a15b937 firewire: Add dev... |
251 |
|
96b19062e firewire: fix "ko... |
252 |
client->device = device; |
19a15b937 firewire: Add dev... |
253 |
spin_lock_init(&client->lock); |
45ee3199e firewire: cdev: u... |
254 255 |
idr_init(&client->resource_idr); INIT_LIST_HEAD(&client->event_list); |
19a15b937 firewire: Add dev... |
256 |
init_waitqueue_head(&client->wait); |
5a5e62da9 firewire: cdev: a... |
257 |
init_waitqueue_head(&client->tx_flush_wait); |
bf54e1462 firewire: cdev: a... |
258 |
INIT_LIST_HEAD(&client->phy_receiver_link); |
93b37905f firewire: cdev: p... |
259 |
INIT_LIST_HEAD(&client->link); |
fb4430367 firewire: cdev: r... |
260 |
kref_init(&client->kref); |
19a15b937 firewire: Add dev... |
261 262 |
file->private_data = client; |
3ac26b2ee firewire: cdev: m... |
263 |
return nonseekable_open(inode, file); |
19a15b937 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 firewire: cdev: u... |
277 278 279 280 |
if (client->in_shutdown) kfree(event); else list_add_tail(&event->link, &client->event_list); |
19a15b937 firewire: Add dev... |
281 |
spin_unlock_irqrestore(&client->lock, flags); |
83431cba3 firewire: fw-cdev... |
282 283 |
wake_up_interruptible(&client->wait); |
19a15b937 firewire: Add dev... |
284 |
} |
53dca5117 firewire: remove ... |
285 286 |
static int dequeue_event(struct client *client, char __user *buffer, size_t count) |
19a15b937 firewire: Add dev... |
287 |
{ |
19a15b937 firewire: Add dev... |
288 289 |
struct event *event; size_t size, total; |
2dbd7d7e2 firewire: standar... |
290 |
int i, ret; |
19a15b937 firewire: Add dev... |
291 |
|
2dbd7d7e2 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 firewire: Add dev... |
297 |
|
2603bf219 firewire: Use onl... |
298 299 300 |
if (list_empty(&client->event_list) && fw_device_is_shutdown(client->device)) return -ENODEV; |
19a15b937 firewire: Add dev... |
301 |
|
3ba949868 firewire: cdev: r... |
302 |
spin_lock_irq(&client->lock); |
a459b8ab9 firewire: cdev: u... |
303 |
event = list_first_entry(&client->event_list, struct event, link); |
19a15b937 firewire: Add dev... |
304 |
list_del(&event->link); |
3ba949868 firewire: cdev: r... |
305 |
spin_unlock_irq(&client->lock); |
19a15b937 firewire: Add dev... |
306 |
|
19a15b937 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 firewire: Use onl... |
310 |
if (copy_to_user(buffer + total, event->v[i].data, size)) { |
2dbd7d7e2 firewire: standar... |
311 |
ret = -EFAULT; |
19a15b937 firewire: Add dev... |
312 |
goto out; |
2603bf219 firewire: Use onl... |
313 |
} |
19a15b937 firewire: Add dev... |
314 315 |
total += size; } |
2dbd7d7e2 firewire: standar... |
316 |
ret = total; |
19a15b937 firewire: Add dev... |
317 318 319 |
out: kfree(event); |
2dbd7d7e2 firewire: standar... |
320 |
return ret; |
19a15b937 firewire: Add dev... |
321 |
} |
53dca5117 firewire: remove ... |
322 323 |
static ssize_t fw_device_op_read(struct file *file, char __user *buffer, size_t count, loff_t *offset) |
19a15b937 firewire: Add dev... |
324 325 326 327 328 |
{ struct client *client = file->private_data; return dequeue_event(client, buffer, count); } |
53dca5117 firewire: remove ... |
329 330 |
static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, struct client *client) |
344bbc4de firewire: General... |
331 |
{ |
da8ecffae firewire: Streaml... |
332 |
struct fw_card *card = client->device->card; |
cf417e549 firewire: add a c... |
333 |
|
3ba949868 firewire: cdev: r... |
334 |
spin_lock_irq(&card->lock); |
344bbc4de firewire: General... |
335 |
|
da8ecffae firewire: Streaml... |
336 |
event->closure = client->bus_reset_closure; |
344bbc4de firewire: General... |
337 |
event->type = FW_CDEV_EVENT_BUS_RESET; |
cf5a56ac8 firewire: fw-cdev... |
338 |
event->generation = client->device->generation; |
da8ecffae firewire: Streaml... |
339 |
event->node_id = client->device->node_id; |
344bbc4de firewire: General... |
340 |
event->local_node_id = card->local_node->node_id; |
250b2b6dd firewire: cdev: f... |
341 |
event->bm_node_id = card->bm_node_id; |
344bbc4de firewire: General... |
342 343 |
event->irm_node_id = card->irm_node->node_id; event->root_node_id = card->root_node->node_id; |
cf417e549 firewire: add a c... |
344 |
|
3ba949868 firewire: cdev: r... |
345 |
spin_unlock_irq(&card->lock); |
344bbc4de firewire: General... |
346 |
} |
53dca5117 firewire: remove ... |
347 348 |
static void for_each_client(struct fw_device *device, void (*callback)(struct client *client)) |
2603bf219 firewire: Use onl... |
349 |
{ |
2603bf219 firewire: Use onl... |
350 |
struct client *c; |
2603bf219 firewire: Use onl... |
351 |
|
d67cfb961 firewire: convert... |
352 |
mutex_lock(&device->client_list_mutex); |
2603bf219 firewire: Use onl... |
353 354 |
list_for_each_entry(c, &device->client_list, link) callback(c); |
d67cfb961 firewire: convert... |
355 |
mutex_unlock(&device->client_list_mutex); |
2603bf219 firewire: Use onl... |
356 |
} |
b1bda4cdc firewire: cdev: a... |
357 358 |
static int schedule_reallocations(int id, void *p, void *data) { |
9fb551bf7 firewire: normali... |
359 |
schedule_if_iso_resource(p); |
b1bda4cdc firewire: cdev: a... |
360 |
|
b1bda4cdc firewire: cdev: a... |
361 362 |
return 0; } |
53dca5117 firewire: remove ... |
363 |
static void queue_bus_reset_event(struct client *client) |
97bd9efa5 firewire: Add a b... |
364 |
{ |
97c18b7fd firewire: cdev: u... |
365 |
struct bus_reset_event *e; |
97bd9efa5 firewire: Add a b... |
366 |
|
97c18b7fd firewire: cdev: u... |
367 |
e = kzalloc(sizeof(*e), GFP_KERNEL); |
cfb0c9d1f firewire: remove ... |
368 |
if (e == NULL) |
97bd9efa5 firewire: Add a b... |
369 |
return; |
97bd9efa5 firewire: Add a b... |
370 |
|
97c18b7fd firewire: cdev: u... |
371 |
fill_bus_reset_event(&e->reset, client); |
97bd9efa5 firewire: Add a b... |
372 |
|
97c18b7fd firewire: cdev: u... |
373 374 |
queue_event(client, &e->event, &e->reset, sizeof(e->reset), NULL, 0); |
b1bda4cdc 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 firewire: Add a b... |
379 380 381 382 |
} void fw_device_cdev_update(struct fw_device *device) { |
2603bf219 firewire: Use onl... |
383 384 |
for_each_client(device, queue_bus_reset_event); } |
97bd9efa5 firewire: Add a b... |
385 |
|
2603bf219 firewire: Use onl... |
386 387 388 389 |
static void wake_up_client(struct client *client) { wake_up_interruptible(&client->wait); } |
97bd9efa5 firewire: Add a b... |
390 |
|
2603bf219 firewire: Use onl... |
391 392 393 |
void fw_device_cdev_remove(struct fw_device *device) { for_each_client(device, wake_up_client); |
97bd9efa5 firewire: Add a b... |
394 |
} |
6e95dea72 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 firewire: cdev: a... |
412 |
struct fw_cdev_send_phy_packet send_phy_packet; |
bf54e1462 firewire: cdev: a... |
413 |
struct fw_cdev_receive_phy_packets receive_phy_packets; |
872e330e3 firewire: add iso... |
414 |
struct fw_cdev_set_iso_channels set_iso_channels; |
d1bbd2097 firewire: allow e... |
415 |
struct fw_cdev_flush_iso flush_iso; |
6e95dea72 firewire: core: c... |
416 417 418 |
}; static int ioctl_get_info(struct client *client, union ioctl_arg *arg) |
19a15b937 firewire: Add dev... |
419 |
{ |
6e95dea72 firewire: core: c... |
420 |
struct fw_cdev_get_info *a = &arg->get_info; |
344bbc4de firewire: General... |
421 |
struct fw_cdev_event_bus_reset bus_reset; |
c9755e14a firewire: reread ... |
422 |
unsigned long ret = 0; |
344bbc4de firewire: General... |
423 |
|
6e95dea72 firewire: core: c... |
424 |
client->version = a->version; |
604f45167 firewire: cdev: f... |
425 |
a->version = FW_CDEV_KERNEL_VERSION; |
6e95dea72 firewire: core: c... |
426 |
a->card = client->device->card->index; |
344bbc4de firewire: General... |
427 |
|
c9755e14a firewire: reread ... |
428 |
down_read(&fw_device_rwsem); |
6e95dea72 firewire: core: c... |
429 430 |
if (a->rom != 0) { size_t want = a->rom_length; |
d84702a5d firewire: fix com... |
431 |
size_t have = client->device->config_rom_length * 4; |
344bbc4de firewire: General... |
432 |
|
6e95dea72 firewire: core: c... |
433 434 |
ret = copy_to_user(u64_to_uptr(a->rom), client->device->config_rom, min(want, have)); |
344bbc4de firewire: General... |
435 |
} |
6e95dea72 firewire: core: c... |
436 |
a->rom_length = client->device->config_rom_length * 4; |
344bbc4de firewire: General... |
437 |
|
c9755e14a firewire: reread ... |
438 439 440 441 |
up_read(&fw_device_rwsem); if (ret != 0) return -EFAULT; |
93b37905f firewire: cdev: p... |
442 |
mutex_lock(&client->device->client_list_mutex); |
6e95dea72 firewire: core: c... |
443 444 |
client->bus_reset_closure = a->bus_reset_closure; if (a->bus_reset != 0) { |
da8ecffae firewire: Streaml... |
445 |
fill_bus_reset_event(&bus_reset, client); |
790198f74 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 firewire: General... |
448 |
} |
93b37905f firewire: cdev: p... |
449 450 |
if (ret == 0 && list_empty(&client->link)) list_add_tail(&client->link, &client->device->client_list); |
19a15b937 firewire: Add dev... |
451 |
|
93b37905f firewire: cdev: p... |
452 453 454 |
mutex_unlock(&client->device->client_list_mutex); return ret ? -EFAULT : 0; |
19a15b937 firewire: Add dev... |
455 |
} |
53dca5117 firewire: remove ... |
456 457 |
static int add_client_resource(struct client *client, struct client_resource *resource, gfp_t gfp_mask) |
3964a4496 firewire: General... |
458 |
{ |
37b61890d firewire: convert... |
459 |
bool preload = gfp_mask & __GFP_WAIT; |
3964a4496 firewire: General... |
460 |
unsigned long flags; |
45ee3199e firewire: cdev: u... |
461 |
int ret; |
37b61890d firewire: convert... |
462 463 |
if (preload) idr_preload(gfp_mask); |
3964a4496 firewire: General... |
464 |
spin_lock_irqsave(&client->lock, flags); |
37b61890d firewire: convert... |
465 |
|
45ee3199e firewire: cdev: u... |
466 467 468 |
if (client->in_shutdown) ret = -ECANCELED; else |
37b61890d firewire: convert... |
469 470 |
ret = idr_alloc(&client->resource_idr, resource, 0, 0, GFP_NOWAIT); |
b1bda4cdc firewire: cdev: a... |
471 |
if (ret >= 0) { |
37b61890d firewire: convert... |
472 |
resource->handle = ret; |
fb4430367 firewire: cdev: r... |
473 |
client_get(client); |
9fb551bf7 firewire: normali... |
474 |
schedule_if_iso_resource(resource); |
b1bda4cdc firewire: cdev: a... |
475 |
} |
45ee3199e firewire: cdev: u... |
476 |
|
37b61890d firewire: convert... |
477 478 479 |
spin_unlock_irqrestore(&client->lock, flags); if (preload) idr_preload_end(); |
45ee3199e firewire: cdev: u... |
480 481 |
return ret < 0 ? ret : 0; |
3964a4496 firewire: General... |
482 |
} |
53dca5117 firewire: remove ... |
483 484 |
static int release_client_resource(struct client *client, u32 handle, client_resource_release_fn_t release, |
e21fcf798 firewire: cdev: n... |
485 |
struct client_resource **return_resource) |
3964a4496 firewire: General... |
486 |
{ |
e21fcf798 firewire: cdev: n... |
487 |
struct client_resource *resource; |
3964a4496 firewire: General... |
488 |
|
3ba949868 firewire: cdev: r... |
489 |
spin_lock_irq(&client->lock); |
45ee3199e firewire: cdev: u... |
490 |
if (client->in_shutdown) |
e21fcf798 firewire: cdev: n... |
491 |
resource = NULL; |
45ee3199e firewire: cdev: u... |
492 |
else |
e21fcf798 firewire: cdev: n... |
493 494 |
resource = idr_find(&client->resource_idr, handle); if (resource && resource->release == release) |
45ee3199e firewire: cdev: u... |
495 |
idr_remove(&client->resource_idr, handle); |
3ba949868 firewire: cdev: r... |
496 |
spin_unlock_irq(&client->lock); |
3964a4496 firewire: General... |
497 |
|
e21fcf798 firewire: cdev: n... |
498 |
if (!(resource && resource->release == release)) |
3964a4496 firewire: General... |
499 |
return -EINVAL; |
e21fcf798 firewire: cdev: n... |
500 501 |
if (return_resource) *return_resource = resource; |
3964a4496 firewire: General... |
502 |
else |
e21fcf798 firewire: cdev: n... |
503 |
resource->release(client, resource); |
3964a4496 firewire: General... |
504 |
|
fb4430367 firewire: cdev: r... |
505 |
client_put(client); |
3964a4496 firewire: General... |
506 507 |
return 0; } |
53dca5117 firewire: remove ... |
508 509 |
static void release_transaction(struct client *client, struct client_resource *resource) |
3964a4496 firewire: General... |
510 |
{ |
3964a4496 firewire: General... |
511 |
} |
53dca5117 firewire: remove ... |
512 513 |
static void complete_transaction(struct fw_card *card, int rcode, void *payload, size_t length, void *data) |
19a15b937 firewire: Add dev... |
514 |
{ |
97c18b7fd 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 firewire: Track p... |
518 |
unsigned long flags; |
19a15b937 firewire: Add dev... |
519 |
|
97c18b7fd firewire: cdev: u... |
520 521 |
if (length < rsp->length) rsp->length = length; |
19a15b937 firewire: Add dev... |
522 |
if (rcode == RCODE_COMPLETE) |
97c18b7fd firewire: cdev: u... |
523 |
memcpy(rsp->data, payload, rsp->length); |
19a15b937 firewire: Add dev... |
524 |
|
28cf6a04c firewire: Track p... |
525 |
spin_lock_irqsave(&client->lock, flags); |
5a5e62da9 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 firewire: Track p... |
529 |
spin_unlock_irqrestore(&client->lock, flags); |
97c18b7fd firewire: cdev: u... |
530 531 |
rsp->type = FW_CDEV_EVENT_RESPONSE; rsp->rcode = rcode; |
8401d92ba firewire: Preserv... |
532 533 |
/* |
97c18b7fd firewire: cdev: u... |
534 |
* In the case that sizeof(*rsp) doesn't align with the position of the |
8401d92ba 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 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 firewire: Preserv... |
543 |
else |
97c18b7fd firewire: cdev: u... |
544 |
queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length, |
8401d92ba firewire: Preserv... |
545 |
NULL, 0); |
fb4430367 firewire: cdev: r... |
546 |
|
5a5e62da9 firewire: cdev: a... |
547 548 |
/* Drop the idr's reference */ client_put(client); |
19a15b937 firewire: Add dev... |
549 |
} |
acfe83335 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 firewire: Add dev... |
553 |
{ |
97c18b7fd firewire: cdev: u... |
554 |
struct outbound_transaction_event *e; |
1f3125af8 firewire: cdev: t... |
555 |
int ret; |
19a15b937 firewire: Add dev... |
556 |
|
18e9b10fc firewire: cdev: a... |
557 558 |
if (request->tcode != TCODE_STREAM_DATA && (request->length > 4096 || request->length > 512 << speed)) |
5d3fd692a firewire: cdev: e... |
559 |
return -EIO; |
19a15b937 firewire: Add dev... |
560 |
|
a8e93f3dc firewire: cdev: c... |
561 562 563 |
if (request->tcode == TCODE_WRITE_QUADLET_REQUEST && request->length < 4) return -EINVAL; |
97c18b7fd firewire: cdev: u... |
564 565 |
e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL); if (e == NULL) |
19a15b937 firewire: Add dev... |
566 |
return -ENOMEM; |
97c18b7fd firewire: cdev: u... |
567 568 569 |
e->client = client; e->response.length = request->length; e->response.closure = request->closure; |
19a15b937 firewire: Add dev... |
570 |
|
4f2592232 firewire: Add rea... |
571 |
if (request->data && |
97c18b7fd firewire: cdev: u... |
572 |
copy_from_user(e->response.data, |
4f2592232 firewire: Add rea... |
573 |
u64_to_uptr(request->data), request->length)) { |
1f3125af8 firewire: cdev: t... |
574 |
ret = -EFAULT; |
45ee3199e firewire: cdev: u... |
575 |
goto failed; |
1f3125af8 firewire: cdev: t... |
576 |
} |
97c18b7fd firewire: cdev: u... |
577 578 |
e->r.resource.release = release_transaction; ret = add_client_resource(client, &e->r.resource, GFP_KERNEL); |
45ee3199e firewire: cdev: u... |
579 580 |
if (ret < 0) goto failed; |
28cf6a04c firewire: Track p... |
581 |
|
acfe83335 firewire: cdev: a... |
582 |
fw_send_request(client->device->card, &e->r.transaction, |
664d8010b 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 firewire: Add dev... |
587 |
|
45ee3199e firewire: cdev: u... |
588 |
failed: |
97c18b7fd firewire: cdev: u... |
589 |
kfree(e); |
1f3125af8 firewire: cdev: t... |
590 591 |
return ret; |
19a15b937 firewire: Add dev... |
592 |
} |
6e95dea72 firewire: core: c... |
593 |
static int ioctl_send_request(struct client *client, union ioctl_arg *arg) |
acfe83335 firewire: cdev: a... |
594 |
{ |
6e95dea72 firewire: core: c... |
595 |
switch (arg->send_request.tcode) { |
acfe83335 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 firewire: core: c... |
611 |
return init_request(client, &arg->send_request, client->device->node_id, |
acfe83335 firewire: cdev: a... |
612 613 |
client->device->max_speed); } |
281e20323 firewire: core: f... |
614 615 616 617 |
static inline bool is_fcp_request(struct fw_request *request) { return request == NULL; } |
53dca5117 firewire: remove ... |
618 619 |
static void release_request(struct client *client, struct client_resource *resource) |
3964a4496 firewire: General... |
620 |
{ |
97c18b7fd firewire: cdev: u... |
621 622 |
struct inbound_transaction_resource *r = container_of(resource, struct inbound_transaction_resource, resource); |
3964a4496 firewire: General... |
623 |
|
281e20323 firewire: core: f... |
624 625 626 |
if (is_fcp_request(r->request)) kfree(r->data); else |
08bd34c98 firewire: cdev: f... |
627 |
fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR); |
0244f5730 firewire: cdev: c... |
628 629 |
fw_card_put(r->card); |
97c18b7fd firewire: cdev: u... |
630 |
kfree(r); |
3964a4496 firewire: General... |
631 |
} |
97c18b7fd firewire: cdev: u... |
632 |
static void handle_request(struct fw_card *card, struct fw_request *request, |
53dca5117 firewire: remove ... |
633 |
int tcode, int destination, int source, |
33e553fe2 firewire: remove ... |
634 |
int generation, unsigned long long offset, |
53dca5117 firewire: remove ... |
635 |
void *payload, size_t length, void *callback_data) |
19a15b937 firewire: Add dev... |
636 |
{ |
97c18b7fd firewire: cdev: u... |
637 638 639 |
struct address_handler_resource *handler = callback_data; struct inbound_transaction_resource *r; struct inbound_transaction_event *e; |
e205597d1 firewire: cdev: f... |
640 |
size_t event_size0; |
281e20323 firewire: core: f... |
641 |
void *fcp_frame = NULL; |
45ee3199e firewire: cdev: u... |
642 |
int ret; |
19a15b937 firewire: Add dev... |
643 |
|
0244f5730 firewire: cdev: c... |
644 645 |
/* card may be different from handler->client->device->card */ fw_card_get(card); |
97c18b7fd firewire: cdev: u... |
646 |
r = kmalloc(sizeof(*r), GFP_ATOMIC); |
2d826cc5c firewire: Always ... |
647 |
e = kmalloc(sizeof(*e), GFP_ATOMIC); |
cfb0c9d1f firewire: remove ... |
648 |
if (r == NULL || e == NULL) |
45ee3199e firewire: cdev: u... |
649 |
goto failed; |
cfb0c9d1f firewire: remove ... |
650 |
|
08bd34c98 firewire: cdev: f... |
651 |
r->card = card; |
97c18b7fd firewire: cdev: u... |
652 653 654 |
r->request = request; r->data = payload; r->length = length; |
19a15b937 firewire: Add dev... |
655 |
|
281e20323 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 firewire: cdev: u... |
667 668 |
r->resource.release = release_request; ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC); |
45ee3199e firewire: cdev: u... |
669 670 |
if (ret < 0) goto failed; |
19a15b937 firewire: Add dev... |
671 |
|
e205597d1 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 firewire: Add dev... |
700 |
|
97c18b7fd firewire: cdev: u... |
701 |
queue_event(handler->client, &e->event, |
e205597d1 firewire: cdev: f... |
702 |
&e->req, event_size0, r->data, length); |
45ee3199e firewire: cdev: u... |
703 704 705 |
return; failed: |
97c18b7fd firewire: cdev: u... |
706 |
kfree(r); |
45ee3199e firewire: cdev: u... |
707 |
kfree(e); |
281e20323 firewire: core: f... |
708 709 710 |
kfree(fcp_frame); if (!is_fcp_request(request)) |
db5d247ae firewire: fix use... |
711 |
fw_send_response(card, request, RCODE_CONFLICT_ERROR); |
0244f5730 firewire: cdev: c... |
712 713 |
fw_card_put(card); |
19a15b937 firewire: Add dev... |
714 |
} |
53dca5117 firewire: remove ... |
715 716 |
static void release_address_handler(struct client *client, struct client_resource *resource) |
3964a4496 firewire: General... |
717 |
{ |
97c18b7fd firewire: cdev: u... |
718 719 |
struct address_handler_resource *r = container_of(resource, struct address_handler_resource, resource); |
3964a4496 firewire: General... |
720 |
|
97c18b7fd firewire: cdev: u... |
721 722 |
fw_core_remove_address_handler(&r->handler); kfree(r); |
3964a4496 firewire: General... |
723 |
} |
6e95dea72 firewire: core: c... |
724 |
static int ioctl_allocate(struct client *client, union ioctl_arg *arg) |
19a15b937 firewire: Add dev... |
725 |
{ |
6e95dea72 firewire: core: c... |
726 |
struct fw_cdev_allocate *a = &arg->allocate; |
97c18b7fd firewire: cdev: u... |
727 |
struct address_handler_resource *r; |
19a15b937 firewire: Add dev... |
728 |
struct fw_address_region region; |
45ee3199e firewire: cdev: u... |
729 |
int ret; |
19a15b937 firewire: Add dev... |
730 |
|
97c18b7fd firewire: cdev: u... |
731 732 |
r = kmalloc(sizeof(*r), GFP_KERNEL); if (r == NULL) |
19a15b937 firewire: Add dev... |
733 |
return -ENOMEM; |
6e95dea72 firewire: core: c... |
734 |
region.start = a->offset; |
8e2b2b46e 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 firewire: core: c... |
739 |
r->handler.length = a->length; |
97c18b7fd firewire: cdev: u... |
740 |
r->handler.address_callback = handle_request; |
6e95dea72 firewire: core: c... |
741 742 743 |
r->handler.callback_data = r; r->closure = a->closure; r->client = client; |
19a15b937 firewire: Add dev... |
744 |
|
97c18b7fd firewire: cdev: u... |
745 |
ret = fw_core_add_address_handler(&r->handler, ®ion); |
3e0b5f0d7 firewire: cdev: a... |
746 |
if (ret < 0) { |
97c18b7fd firewire: cdev: u... |
747 |
kfree(r); |
3e0b5f0d7 firewire: cdev: a... |
748 |
return ret; |
19a15b937 firewire: Add dev... |
749 |
} |
8e2b2b46e firewire: cdev: i... |
750 |
a->offset = r->handler.offset; |
19a15b937 firewire: Add dev... |
751 |
|
97c18b7fd firewire: cdev: u... |
752 753 |
r->resource.release = release_address_handler; ret = add_client_resource(client, &r->resource, GFP_KERNEL); |
45ee3199e firewire: cdev: u... |
754 |
if (ret < 0) { |
97c18b7fd firewire: cdev: u... |
755 |
release_address_handler(client, &r->resource); |
45ee3199e firewire: cdev: u... |
756 757 |
return ret; } |
6e95dea72 firewire: core: c... |
758 |
a->handle = r->resource.handle; |
19a15b937 firewire: Add dev... |
759 760 761 |
return 0; } |
6e95dea72 firewire: core: c... |
762 |
static int ioctl_deallocate(struct client *client, union ioctl_arg *arg) |
9472316b6 firewire: Impleme... |
763 |
{ |
6e95dea72 firewire: core: c... |
764 |
return release_client_resource(client, arg->deallocate.handle, |
45ee3199e firewire: cdev: u... |
765 |
release_address_handler, NULL); |
9472316b6 firewire: Impleme... |
766 |
} |
6e95dea72 firewire: core: c... |
767 |
static int ioctl_send_response(struct client *client, union ioctl_arg *arg) |
19a15b937 firewire: Add dev... |
768 |
{ |
6e95dea72 firewire: core: c... |
769 |
struct fw_cdev_send_response *a = &arg->send_response; |
3964a4496 firewire: General... |
770 |
struct client_resource *resource; |
97c18b7fd firewire: cdev: u... |
771 |
struct inbound_transaction_resource *r; |
7e44c0b56 firewire: cdev: f... |
772 |
int ret = 0; |
19a15b937 firewire: Add dev... |
773 |
|
6e95dea72 firewire: core: c... |
774 |
if (release_client_resource(client, a->handle, |
45ee3199e firewire: cdev: u... |
775 |
release_request, &resource) < 0) |
19a15b937 firewire: Add dev... |
776 |
return -EINVAL; |
45ee3199e firewire: cdev: u... |
777 |
|
97c18b7fd firewire: cdev: u... |
778 779 |
r = container_of(resource, struct inbound_transaction_resource, resource); |
281e20323 firewire: core: f... |
780 781 |
if (is_fcp_request(r->request)) goto out; |
a10c0ce76 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 firewire: core: f... |
788 789 790 |
ret = -EFAULT; kfree(r->request); goto out; |
7e44c0b56 firewire: cdev: f... |
791 |
} |
08bd34c98 firewire: cdev: f... |
792 |
fw_send_response(r->card, r->request, a->rcode); |
7e44c0b56 firewire: cdev: f... |
793 |
out: |
0244f5730 firewire: cdev: c... |
794 |
fw_card_put(r->card); |
19a15b937 firewire: Add dev... |
795 |
kfree(r); |
7e44c0b56 firewire: cdev: f... |
796 |
return ret; |
19a15b937 firewire: Add dev... |
797 |
} |
6e95dea72 firewire: core: c... |
798 |
static int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg) |
5371842b7 firewire: Impleme... |
799 |
{ |
02d37bed1 firewire: core: i... |
800 |
fw_schedule_bus_reset(client->device->card, true, |
6e95dea72 firewire: core: c... |
801 |
arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET); |
02d37bed1 firewire: core: i... |
802 |
return 0; |
5371842b7 firewire: Impleme... |
803 |
} |
3964a4496 firewire: General... |
804 805 806 |
static void release_descriptor(struct client *client, struct client_resource *resource) { |
97c18b7fd firewire: cdev: u... |
807 808 |
struct descriptor_resource *r = container_of(resource, struct descriptor_resource, resource); |
3964a4496 firewire: General... |
809 |
|
97c18b7fd firewire: cdev: u... |
810 811 |
fw_core_remove_descriptor(&r->descriptor); kfree(r); |
3964a4496 firewire: General... |
812 |
} |
6e95dea72 firewire: core: c... |
813 |
static int ioctl_add_descriptor(struct client *client, union ioctl_arg *arg) |
66dea3e5f firewire: Add ioc... |
814 |
{ |
6e95dea72 firewire: core: c... |
815 |
struct fw_cdev_add_descriptor *a = &arg->add_descriptor; |
97c18b7fd firewire: cdev: u... |
816 |
struct descriptor_resource *r; |
45ee3199e firewire: cdev: u... |
817 |
int ret; |
66dea3e5f firewire: Add ioc... |
818 |
|
de487da8c firewire: cdev: s... |
819 |
/* Access policy: Allow this ioctl only on local nodes' device files. */ |
92368890d firewire: core: i... |
820 |
if (!client->device->is_local) |
de487da8c firewire: cdev: s... |
821 |
return -ENOSYS; |
6e95dea72 firewire: core: c... |
822 |
if (a->length > 256) |
66dea3e5f firewire: Add ioc... |
823 |
return -EINVAL; |
6e95dea72 firewire: core: c... |
824 |
r = kmalloc(sizeof(*r) + a->length * 4, GFP_KERNEL); |
97c18b7fd firewire: cdev: u... |
825 |
if (r == NULL) |
66dea3e5f firewire: Add ioc... |
826 |
return -ENOMEM; |
6e95dea72 firewire: core: c... |
827 |
if (copy_from_user(r->data, u64_to_uptr(a->data), a->length * 4)) { |
45ee3199e firewire: cdev: u... |
828 829 |
ret = -EFAULT; goto failed; |
66dea3e5f firewire: Add ioc... |
830 |
} |
6e95dea72 firewire: core: c... |
831 832 833 |
r->descriptor.length = a->length; r->descriptor.immediate = a->immediate; r->descriptor.key = a->key; |
97c18b7fd firewire: cdev: u... |
834 |
r->descriptor.data = r->data; |
66dea3e5f firewire: Add ioc... |
835 |
|
97c18b7fd firewire: cdev: u... |
836 |
ret = fw_core_add_descriptor(&r->descriptor); |
45ee3199e firewire: cdev: u... |
837 838 |
if (ret < 0) goto failed; |
66dea3e5f firewire: Add ioc... |
839 |
|
97c18b7fd firewire: cdev: u... |
840 841 |
r->resource.release = release_descriptor; ret = add_client_resource(client, &r->resource, GFP_KERNEL); |
45ee3199e firewire: cdev: u... |
842 |
if (ret < 0) { |
97c18b7fd firewire: cdev: u... |
843 |
fw_core_remove_descriptor(&r->descriptor); |
45ee3199e firewire: cdev: u... |
844 845 |
goto failed; } |
6e95dea72 firewire: core: c... |
846 |
a->handle = r->resource.handle; |
66dea3e5f firewire: Add ioc... |
847 848 |
return 0; |
45ee3199e firewire: cdev: u... |
849 |
failed: |
97c18b7fd firewire: cdev: u... |
850 |
kfree(r); |
45ee3199e firewire: cdev: u... |
851 852 |
return ret; |
66dea3e5f firewire: Add ioc... |
853 |
} |
6e95dea72 firewire: core: c... |
854 |
static int ioctl_remove_descriptor(struct client *client, union ioctl_arg *arg) |
66dea3e5f firewire: Add ioc... |
855 |
{ |
6e95dea72 firewire: core: c... |
856 |
return release_client_resource(client, arg->remove_descriptor.handle, |
45ee3199e firewire: cdev: u... |
857 |
release_descriptor, NULL); |
66dea3e5f firewire: Add ioc... |
858 |
} |
53dca5117 firewire: remove ... |
859 860 |
static void iso_callback(struct fw_iso_context *context, u32 cycle, size_t header_length, void *header, void *data) |
19a15b937 firewire: Add dev... |
861 862 |
{ struct client *client = data; |
97c18b7fd firewire: cdev: u... |
863 |
struct iso_interrupt_event *e; |
19a15b937 firewire: Add dev... |
864 |
|
56d04cb18 firewire: core: r... |
865 |
e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); |
cfb0c9d1f firewire: remove ... |
866 |
if (e == NULL) |
19a15b937 firewire: Add dev... |
867 |
return; |
cfb0c9d1f firewire: remove ... |
868 |
|
97c18b7fd 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 firewire: Add dev... |
876 |
} |
872e330e3 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 firewire: remove ... |
884 |
if (e == NULL) |
872e330e3 firewire: add iso... |
885 |
return; |
cfb0c9d1f firewire: remove ... |
886 |
|
872e330e3 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 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 firewire: core: c... |
901 |
static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg) |
19a15b937 firewire: Add dev... |
902 |
{ |
6e95dea72 firewire: core: c... |
903 |
struct fw_cdev_create_iso_context *a = &arg->create_iso_context; |
24315c5e6 firewire: Only se... |
904 |
struct fw_iso_context *context; |
872e330e3 firewire: add iso... |
905 |
fw_iso_callback_t cb; |
0b6c4857f firewire: core: f... |
906 |
int ret; |
19a15b937 firewire: Add dev... |
907 |
|
eb5b35a56 firewire: core: e... |
908 |
BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT || |
872e330e3 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 firewire: Configu... |
912 |
|
6e95dea72 firewire: core: c... |
913 |
switch (a->type) { |
872e330e3 firewire: add iso... |
914 915 |
case FW_ISO_CONTEXT_TRANSMIT: if (a->speed > SCODE_3200 || a->channel > 63) |
c70dc788f firewire: Fix dua... |
916 |
return -EINVAL; |
872e330e3 firewire: add iso... |
917 918 |
cb = iso_callback; |
c70dc788f firewire: Fix dua... |
919 |
break; |
872e330e3 firewire: add iso... |
920 921 922 |
case FW_ISO_CONTEXT_RECEIVE: if (a->header_size < 4 || (a->header_size & 3) || a->channel > 63) |
c70dc788f firewire: Fix dua... |
923 |
return -EINVAL; |
872e330e3 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 firewire: Fix dua... |
930 931 932 |
break; default: |
21efb3cfc firewire: Configu... |
933 |
return -EINVAL; |
c70dc788f firewire: Fix dua... |
934 |
} |
6e95dea72 firewire: core: c... |
935 |
context = fw_iso_context_create(client->device->card, a->type, |
872e330e3 firewire: add iso... |
936 |
a->channel, a->speed, a->header_size, cb, client); |
24315c5e6 firewire: Only se... |
937 938 |
if (IS_ERR(context)) return PTR_ERR(context); |
bdfe273ee 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 firewire: core: f... |
944 |
|
bdfe273ee firewire: cdev: f... |
945 946 |
return -EBUSY; } |
0b6c4857f 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 firewire: core: c... |
959 |
client->iso_closure = a->closure; |
24315c5e6 firewire: Only se... |
960 |
client->iso_context = context; |
bdfe273ee firewire: cdev: f... |
961 |
spin_unlock_irq(&client->lock); |
19a15b937 firewire: Add dev... |
962 |
|
6e95dea72 firewire: core: c... |
963 |
a->handle = 0; |
abaa5743e firewire: Future ... |
964 |
|
19a15b937 firewire: Add dev... |
965 966 |
return 0; } |
872e330e3 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 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 firewire: fix set... |
981 982 |
#define GET_TAG(v) (((v) >> 18) & 0x03) #define GET_SY(v) (((v) >> 20) & 0x0f) |
1ca31ae7c firewire: Change ... |
983 |
#define GET_HEADER_LENGTH(v) (((v) >> 24) & 0xff) |
6e95dea72 firewire: core: c... |
984 |
static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg) |
19a15b937 firewire: Add dev... |
985 |
{ |
6e95dea72 firewire: core: c... |
986 |
struct fw_cdev_queue_iso *a = &arg->queue_iso; |
19a15b937 firewire: Add dev... |
987 |
struct fw_cdev_iso_packet __user *p, *end, *next; |
9b32d5f30 firewire: Acummul... |
988 |
struct fw_iso_context *ctx = client->iso_context; |
872e330e3 firewire: add iso... |
989 |
unsigned long payload, buffer_end, transmit_header_bytes = 0; |
1ca31ae7c firewire: Change ... |
990 |
u32 control; |
19a15b937 firewire: Add dev... |
991 992 993 994 995 |
int count; struct { struct fw_iso_packet packet; u8 header[256]; } u; |
6e95dea72 firewire: core: c... |
996 |
if (ctx == NULL || a->handle != 0) |
19a15b937 firewire: Add dev... |
997 |
return -EINVAL; |
19a15b937 firewire: Add dev... |
998 |
|
c781c06d1 firewire: Clean u... |
999 1000 |
/* * If the user passes a non-NULL data pointer, has mmap()'ed |
19a15b937 firewire: Add dev... |
1001 1002 |
* the iso buffer, and the pointer points inside the buffer, * we setup the payload pointers accordingly. Otherwise we |
9aad81253 firewire: Split t... |
1003 |
* set them both to 0, which will still let packets with |
19a15b937 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 firewire: core: c... |
1006 |
* and the a->data pointer is ignored. |
c781c06d1 firewire: Clean u... |
1007 |
*/ |
6e95dea72 firewire: core: c... |
1008 |
payload = (unsigned long)a->data - client->vm_start; |
ef370ee74 firewire: Fix the... |
1009 |
buffer_end = client->buffer.page_count << PAGE_SHIFT; |
6e95dea72 firewire: core: c... |
1010 |
if (a->data == 0 || client->buffer.pages == NULL || |
ef370ee74 firewire: Fix the... |
1011 |
payload >= buffer_end) { |
9aad81253 firewire: Split t... |
1012 |
payload = 0; |
ef370ee74 firewire: Fix the... |
1013 |
buffer_end = 0; |
19a15b937 firewire: Add dev... |
1014 |
} |
872e330e3 firewire: add iso... |
1015 1016 |
if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3) return -EINVAL; |
1ccc9147f fw-cdev __user an... |
1017 |
|
872e330e3 firewire: add iso... |
1018 |
p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets); |
6e95dea72 firewire: core: c... |
1019 |
if (!access_ok(VERIFY_READ, p, a->size)) |
19a15b937 firewire: Add dev... |
1020 |
return -EFAULT; |
6e95dea72 firewire: core: c... |
1021 |
end = (void __user *)p + a->size; |
19a15b937 firewire: Add dev... |
1022 1023 |
count = 0; while (p < end) { |
1ca31ae7c firewire: Change ... |
1024 |
if (get_user(control, &p->control)) |
19a15b937 firewire: Add dev... |
1025 |
return -EFAULT; |
1ca31ae7c 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 firewire: Impleme... |
1032 |
|
872e330e3 firewire: add iso... |
1033 1034 1035 |
switch (ctx->type) { case FW_ISO_CONTEXT_TRANSMIT: if (u.packet.header_length & 3) |
385ab5bcd firewire: cdev: r... |
1036 |
return -EINVAL; |
ae2a97661 firewire: core: s... |
1037 |
transmit_header_bytes = u.packet.header_length; |
872e330e3 firewire: add iso... |
1038 1039 1040 |
break; case FW_ISO_CONTEXT_RECEIVE: |
69e61d0c0 firewire: core: r... |
1041 1042 |
if (u.packet.header_length == 0 || u.packet.header_length % ctx->header_size != 0) |
385ab5bcd firewire: cdev: r... |
1043 |
return -EINVAL; |
872e330e3 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 firewire: Impleme... |
1049 |
return -EINVAL; |
872e330e3 firewire: add iso... |
1050 |
break; |
295e3feb9 firewire: Impleme... |
1051 |
} |
19a15b937 firewire: Add dev... |
1052 |
next = (struct fw_cdev_iso_packet __user *) |
ae2a97661 firewire: core: s... |
1053 |
&p->header[transmit_header_bytes / 4]; |
19a15b937 firewire: Add dev... |
1054 1055 1056 |
if (next > end) return -EINVAL; if (__copy_from_user |
ae2a97661 firewire: core: s... |
1057 |
(u.packet.header, p->header, transmit_header_bytes)) |
19a15b937 firewire: Add dev... |
1058 |
return -EFAULT; |
98b6cbe83 firewire: Impleme... |
1059 |
if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT && |
19a15b937 firewire: Add dev... |
1060 1061 |
u.packet.header_length + u.packet.payload_length > 0) return -EINVAL; |
ef370ee74 firewire: Fix the... |
1062 |
if (payload + u.packet.payload_length > buffer_end) |
19a15b937 firewire: Add dev... |
1063 |
return -EINVAL; |
9b32d5f30 firewire: Acummul... |
1064 1065 |
if (fw_iso_context_queue(ctx, &u.packet, &client->buffer, payload)) |
19a15b937 firewire: Add dev... |
1066 1067 1068 1069 1070 1071 |
break; p = next; payload += u.packet.payload_length; count++; } |
13882a82e firewire: optimiz... |
1072 |
fw_iso_context_queue_flush(ctx); |
19a15b937 firewire: Add dev... |
1073 |
|
6e95dea72 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 firewire: Add dev... |
1077 1078 1079 |
return count; } |
6e95dea72 firewire: core: c... |
1080 |
static int ioctl_start_iso(struct client *client, union ioctl_arg *arg) |
19a15b937 firewire: Add dev... |
1081 |
{ |
6e95dea72 firewire: core: c... |
1082 |
struct fw_cdev_start_iso *a = &arg->start_iso; |
19a15b937 firewire: Add dev... |
1083 |
|
eb5b35a56 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 firewire: core: c... |
1090 |
if (client->iso_context == NULL || a->handle != 0) |
abaa5743e firewire: Future ... |
1091 |
return -EINVAL; |
fae603121 firewire: fix NUL... |
1092 |
|
6e95dea72 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 firewire: Move sy... |
1096 |
|
6e95dea72 firewire: core: c... |
1097 1098 |
return fw_iso_context_start(client->iso_context, a->cycle, a->sync, a->tags); |
19a15b937 firewire: Add dev... |
1099 |
} |
6e95dea72 firewire: core: c... |
1100 |
static int ioctl_stop_iso(struct client *client, union ioctl_arg *arg) |
b82956685 firewire: Impleme... |
1101 |
{ |
6e95dea72 firewire: core: c... |
1102 |
struct fw_cdev_stop_iso *a = &arg->stop_iso; |
abaa5743e firewire: Future ... |
1103 |
|
6e95dea72 firewire: core: c... |
1104 |
if (client->iso_context == NULL || a->handle != 0) |
abaa5743e firewire: Future ... |
1105 |
return -EINVAL; |
b82956685 firewire: Impleme... |
1106 1107 |
return fw_iso_context_stop(client->iso_context); } |
d1bbd2097 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 firewire: core: c... |
1117 |
static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg) |
a64408b96 firewire: adopt r... |
1118 |
{ |
6e95dea72 firewire: core: c... |
1119 |
struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2; |
a64408b96 firewire: adopt r... |
1120 |
struct fw_card *card = client->device->card; |
abfe5a01e firewire: cdev: a... |
1121 |
struct timespec ts = {0, 0}; |
4a9bde9b8 firewire: get_cyc... |
1122 |
u32 cycle_time; |
abfe5a01e firewire: cdev: a... |
1123 |
int ret = 0; |
a64408b96 firewire: adopt r... |
1124 |
|
4a9bde9b8 firewire: get_cyc... |
1125 |
local_irq_disable(); |
a64408b96 firewire: adopt r... |
1126 |
|
0fcff4e39 firewire: rename ... |
1127 |
cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME); |
abfe5a01e firewire: cdev: a... |
1128 |
|
6e95dea72 firewire: core: c... |
1129 |
switch (a->clk_id) { |
abfe5a01e 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 firewire: adopt r... |
1136 |
|
4a9bde9b8 firewire: get_cyc... |
1137 |
local_irq_enable(); |
a64408b96 firewire: adopt r... |
1138 |
|
6e95dea72 firewire: core: c... |
1139 1140 1141 |
a->tv_sec = ts.tv_sec; a->tv_nsec = ts.tv_nsec; a->cycle_timer = cycle_time; |
abfe5a01e firewire: cdev: a... |
1142 1143 1144 |
return ret; } |
6e95dea72 firewire: core: c... |
1145 |
static int ioctl_get_cycle_timer(struct client *client, union ioctl_arg *arg) |
abfe5a01e firewire: cdev: a... |
1146 |
{ |
6e95dea72 firewire: core: c... |
1147 |
struct fw_cdev_get_cycle_timer *a = &arg->get_cycle_timer; |
abfe5a01e firewire: cdev: a... |
1148 1149 1150 |
struct fw_cdev_get_cycle_timer2 ct2; ct2.clk_id = CLOCK_REALTIME; |
6e95dea72 firewire: core: c... |
1151 |
ioctl_get_cycle_timer2(client, (union ioctl_arg *)&ct2); |
abfe5a01e firewire: cdev: a... |
1152 |
|
6e95dea72 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 firewire: get_cyc... |
1155 |
|
a64408b96 firewire: adopt r... |
1156 1157 |
return 0; } |
b1bda4cdc 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 firewire: core: f... |
1172 1173 |
time_before64(get_jiffies_64(), client->device->card->reset_jiffies + HZ)) { |
9fb551bf7 firewire: normali... |
1174 |
schedule_iso_resource(r, DIV_ROUND_UP(HZ, 3)); |
b1bda4cdc 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 firewire: cdev: a... |
1181 1182 1183 |
free = todo == ISO_RES_DEALLOC || todo == ISO_RES_ALLOC_ONCE || todo == ISO_RES_DEALLOC_ONCE; |
b1bda4cdc 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 firewire: cdev: a... |
1194 1195 |
todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC || |
f30e6d3e4 firewire: octlet ... |
1196 |
todo == ISO_RES_ALLOC_ONCE); |
b1bda4cdc 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 firewire: cdev: a... |
1229 |
r->channels = 1ULL << channel; |
b1bda4cdc firewire: cdev: a... |
1230 1231 1232 |
if (todo == ISO_RES_REALLOC && success) goto out; |
1ec3c0269 firewire: cdev: a... |
1233 |
if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) { |
b1bda4cdc 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 firewire: cdev: n... |
1240 1241 1242 |
e->iso_resource.handle = r->resource.handle; e->iso_resource.channel = channel; e->iso_resource.bandwidth = bandwidth; |
b1bda4cdc firewire: cdev: a... |
1243 1244 |
queue_event(client, &e->event, |
e21fcf798 firewire: cdev: n... |
1245 |
&e->iso_resource, sizeof(e->iso_resource), NULL, 0); |
b1bda4cdc 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 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 firewire: normali... |
1264 |
schedule_iso_resource(r, 0); |
b1bda4cdc firewire: cdev: a... |
1265 1266 |
spin_unlock_irq(&client->lock); } |
1ec3c0269 firewire: cdev: a... |
1267 1268 |
static int init_iso_resource(struct client *client, struct fw_cdev_allocate_iso_resource *request, int todo) |
b1bda4cdc firewire: cdev: a... |
1269 |
{ |
b1bda4cdc 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 firewire: core: r... |
1275 |
request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL) |
b1bda4cdc 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 firewire: cdev: a... |
1288 |
r->todo = todo; |
b1bda4cdc 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 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 firewire: cdev: a... |
1298 |
|
1ec3c0269 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 firewire: cdev: s... |
1302 1303 |
if (ret < 0) goto fail; |
1ec3c0269 firewire: cdev: a... |
1304 1305 1306 |
} else { r->resource.release = NULL; r->resource.handle = -1; |
9fb551bf7 firewire: normali... |
1307 |
schedule_iso_resource(r, 0); |
1ec3c0269 firewire: cdev: a... |
1308 |
} |
b1bda4cdc 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 firewire: core: c... |
1319 1320 |
static int ioctl_allocate_iso_resource(struct client *client, union ioctl_arg *arg) |
1ec3c0269 firewire: cdev: a... |
1321 |
{ |
6e95dea72 firewire: core: c... |
1322 1323 |
return init_iso_resource(client, &arg->allocate_iso_resource, ISO_RES_ALLOC); |
1ec3c0269 firewire: cdev: a... |
1324 |
} |
6e95dea72 firewire: core: c... |
1325 1326 |
static int ioctl_deallocate_iso_resource(struct client *client, union ioctl_arg *arg) |
b1bda4cdc firewire: cdev: a... |
1327 |
{ |
6e95dea72 firewire: core: c... |
1328 1329 |
return release_client_resource(client, arg->deallocate.handle, release_iso_resource, NULL); |
b1bda4cdc firewire: cdev: a... |
1330 |
} |
6e95dea72 firewire: core: c... |
1331 1332 |
static int ioctl_allocate_iso_resource_once(struct client *client, union ioctl_arg *arg) |
1ec3c0269 firewire: cdev: a... |
1333 |
{ |
6e95dea72 firewire: core: c... |
1334 1335 |
return init_iso_resource(client, &arg->allocate_iso_resource, ISO_RES_ALLOC_ONCE); |
1ec3c0269 firewire: cdev: a... |
1336 |
} |
6e95dea72 firewire: core: c... |
1337 1338 |
static int ioctl_deallocate_iso_resource_once(struct client *client, union ioctl_arg *arg) |
1ec3c0269 firewire: cdev: a... |
1339 |
{ |
6e95dea72 firewire: core: c... |
1340 1341 |
return init_iso_resource(client, &arg->allocate_iso_resource, ISO_RES_DEALLOC_ONCE); |
1ec3c0269 firewire: cdev: a... |
1342 |
} |
c8a25900f 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 firewire: core: c... |
1348 |
static int ioctl_get_speed(struct client *client, union ioctl_arg *arg) |
33580a3ef firewire: cdev: a... |
1349 |
{ |
c8a25900f firewire: cdev: a... |
1350 |
return client->device->max_speed; |
33580a3ef firewire: cdev: a... |
1351 |
} |
6e95dea72 firewire: core: c... |
1352 1353 |
static int ioctl_send_broadcast_request(struct client *client, union ioctl_arg *arg) |
acfe83335 firewire: cdev: a... |
1354 |
{ |
6e95dea72 firewire: core: c... |
1355 |
struct fw_cdev_send_request *a = &arg->send_request; |
acfe83335 firewire: cdev: a... |
1356 |
|
6e95dea72 firewire: core: c... |
1357 |
switch (a->tcode) { |
acfe83335 firewire: cdev: a... |
1358 1359 1360 1361 1362 1363 |
case TCODE_WRITE_QUADLET_REQUEST: case TCODE_WRITE_BLOCK_REQUEST: break; default: return -EINVAL; } |
1566f3dc3 firewire: cdev: r... |
1364 |
/* Security policy: Only allow accesses to Units Space. */ |
6e95dea72 firewire: core: c... |
1365 |
if (a->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END) |
1566f3dc3 firewire: cdev: r... |
1366 |
return -EACCES; |
6e95dea72 firewire: core: c... |
1367 |
return init_request(client, a, LOCAL_BUS | 0x3f, SCODE_100); |
acfe83335 firewire: cdev: a... |
1368 |
} |
6e95dea72 firewire: core: c... |
1369 |
static int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg) |
f8c2287c6 firewire: impleme... |
1370 |
{ |
6e95dea72 firewire: core: c... |
1371 |
struct fw_cdev_send_stream_packet *a = &arg->send_stream_packet; |
18e9b10fc firewire: cdev: a... |
1372 1373 |
struct fw_cdev_send_request request; int dest; |
f8c2287c6 firewire: impleme... |
1374 |
|
6e95dea72 firewire: core: c... |
1375 1376 |
if (a->speed > client->device->card->link_speed || a->length > 1024 << a->speed) |
18e9b10fc firewire: cdev: a... |
1377 |
return -EIO; |
f8c2287c6 firewire: impleme... |
1378 |
|
6e95dea72 firewire: core: c... |
1379 |
if (a->tag > 3 || a->channel > 63 || a->sy > 15) |
18e9b10fc firewire: cdev: a... |
1380 |
return -EINVAL; |
6e95dea72 firewire: core: c... |
1381 |
dest = fw_stream_packet_destination_id(a->tag, a->channel, a->sy); |
18e9b10fc firewire: cdev: a... |
1382 |
request.tcode = TCODE_STREAM_DATA; |
6e95dea72 firewire: core: c... |
1383 1384 1385 1386 |
request.length = a->length; request.closure = a->closure; request.data = a->data; request.generation = a->generation; |
18e9b10fc firewire: cdev: a... |
1387 |
|
6e95dea72 firewire: core: c... |
1388 |
return init_request(client, &request, dest, a->speed); |
f8c2287c6 firewire: impleme... |
1389 |
} |
850bb6f23 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 firewire: cdev: a... |
1409 |
e->phy_packet.data[0] = packet->timestamp; |
850bb6f23 firewire: cdev: a... |
1410 |
|
cc550216a 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 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 firewire: cdev: a... |
1425 |
e = kzalloc(sizeof(*e) + 4, GFP_KERNEL); |
850bb6f23 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 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 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 firewire: cdev: a... |
1440 1441 |
if (is_ping_packet(a->data)) e->phy_packet.length = 4; |
850bb6f23 firewire: cdev: a... |
1442 1443 1444 1445 1446 |
card->driver->send_request(card, &e->p); return 0; } |
bf54e1462 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 firewire: remove ... |
1476 |
if (e == NULL) |
bf54e1462 firewire: cdev: a... |
1477 |
break; |
cfb0c9d1f firewire: remove ... |
1478 |
|
bf54e1462 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 firewire: core: c... |
1491 |
static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { |
b9dc61cf4 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 firewire: cdev: a... |
1513 |
[0x15] = ioctl_send_phy_packet, |
bf54e1462 firewire: cdev: a... |
1514 |
[0x16] = ioctl_receive_phy_packets, |
872e330e3 firewire: add iso... |
1515 |
[0x17] = ioctl_set_iso_channels, |
d1bbd2097 firewire: allow e... |
1516 |
[0x18] = ioctl_flush_iso, |
4f2592232 firewire: Add rea... |
1517 |
}; |
53dca5117 firewire: remove ... |
1518 1519 |
static int dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg) |
19a15b937 firewire: Add dev... |
1520 |
{ |
6e95dea72 firewire: core: c... |
1521 |
union ioctl_arg buffer; |
2dbd7d7e2 firewire: standar... |
1522 |
int ret; |
4f2592232 firewire: Add rea... |
1523 |
|
64582298b firewire: core: c... |
1524 1525 |
if (fw_device_is_shutdown(client->device)) return -ENODEV; |
4f2592232 firewire: Add rea... |
1526 |
if (_IOC_TYPE(cmd) != '#' || |
9cac00b8f firewire: cdev: f... |
1527 1528 |
_IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) || _IOC_SIZE(cmd) > sizeof(buffer)) |
d873d7942 firewire: cdev: r... |
1529 |
return -ENOTTY; |
4f2592232 firewire: Add rea... |
1530 |
|
9cac00b8f 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 firewire: Add rea... |
1536 |
return -EFAULT; |
4f2592232 firewire: Add rea... |
1537 |
|
6e95dea72 firewire: core: c... |
1538 |
ret = ioctl_handlers[_IOC_NR(cmd)](client, &buffer); |
2dbd7d7e2 firewire: standar... |
1539 1540 |
if (ret < 0) return ret; |
4f2592232 firewire: Add rea... |
1541 |
|
9cac00b8f firewire: cdev: f... |
1542 1543 |
if (_IOC_DIR(cmd) & _IOC_READ) if (copy_to_user(arg, &buffer, _IOC_SIZE(cmd))) |
4f2592232 firewire: Add rea... |
1544 |
return -EFAULT; |
4f2592232 firewire: Add rea... |
1545 |
|
2dbd7d7e2 firewire: standar... |
1546 |
return ret; |
19a15b937 firewire: Add dev... |
1547 |
} |
53dca5117 firewire: remove ... |
1548 1549 |
static long fw_device_op_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
19a15b937 firewire: Add dev... |
1550 |
{ |
64582298b firewire: core: c... |
1551 |
return dispatch_ioctl(file->private_data, cmd, (void __user *)arg); |
19a15b937 firewire: Add dev... |
1552 1553 1554 |
} #ifdef CONFIG_COMPAT |
53dca5117 firewire: remove ... |
1555 1556 |
static long fw_device_op_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
19a15b937 firewire: Add dev... |
1557 |
{ |
64582298b firewire: core: c... |
1558 |
return dispatch_ioctl(file->private_data, cmd, compat_ptr(arg)); |
19a15b937 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 firewire: Split t... |
1565 |
unsigned long size; |
2dbd7d7e2 firewire: standar... |
1566 |
int page_count, ret; |
9aad81253 firewire: Split t... |
1567 |
|
551f4cb9d firewire: prevent... |
1568 1569 |
if (fw_device_is_shutdown(client->device)) return -ENODEV; |
9aad81253 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 firewire: Add dev... |
1576 |
|
9aad81253 firewire: Split t... |
1577 |
if (vma->vm_start & ~PAGE_MASK) |
19a15b937 firewire: Add dev... |
1578 1579 1580 |
return -EINVAL; client->vm_start = vma->vm_start; |
9aad81253 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 firewire: core: f... |
1585 |
ret = fw_iso_buffer_alloc(&client->buffer, page_count); |
2dbd7d7e2 firewire: standar... |
1586 1587 |
if (ret < 0) return ret; |
19a15b937 firewire: Add dev... |
1588 |
|
0b6c4857f 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 firewire: standar... |
1597 |
if (ret < 0) |
0b6c4857f firewire: core: f... |
1598 |
goto fail; |
9aad81253 firewire: Split t... |
1599 |
|
0b6c4857f 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 firewire: standar... |
1607 |
return ret; |
19a15b937 firewire: Add dev... |
1608 |
} |
5a5e62da9 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 firewire: cdev: u... |
1627 1628 |
static int shutdown_resource(int id, void *p, void *data) { |
e21fcf798 firewire: cdev: n... |
1629 |
struct client_resource *resource = p; |
45ee3199e firewire: cdev: u... |
1630 |
struct client *client = data; |
e21fcf798 firewire: cdev: n... |
1631 |
resource->release(client, resource); |
fb4430367 firewire: cdev: r... |
1632 |
client_put(client); |
45ee3199e firewire: cdev: u... |
1633 1634 1635 |
return 0; } |
19a15b937 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 firewire: cdev: n... |
1639 |
struct event *event, *next_event; |
19a15b937 firewire: Add dev... |
1640 |
|
bf54e1462 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 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 firewire: Add dev... |
1647 1648 |
if (client->iso_context) fw_iso_context_destroy(client->iso_context); |
36a755cfc firewire: cdev: s... |
1649 1650 |
if (client->buffer.pages) fw_iso_buffer_destroy(&client->buffer, client->device->card); |
45ee3199e firewire: cdev: u... |
1651 |
/* Freeze client->resource_idr and client->event_list */ |
3ba949868 firewire: cdev: r... |
1652 |
spin_lock_irq(&client->lock); |
45ee3199e firewire: cdev: u... |
1653 |
client->in_shutdown = true; |
3ba949868 firewire: cdev: r... |
1654 |
spin_unlock_irq(&client->lock); |
66dea3e5f firewire: Add ioc... |
1655 |
|
5a5e62da9 firewire: cdev: a... |
1656 |
wait_event(client->tx_flush_wait, !has_outbound_transactions(client)); |
45ee3199e firewire: cdev: u... |
1657 |
idr_for_each(&client->resource_idr, shutdown_resource, client); |
45ee3199e firewire: cdev: u... |
1658 |
idr_destroy(&client->resource_idr); |
28cf6a04c firewire: Track p... |
1659 |
|
e21fcf798 firewire: cdev: n... |
1660 1661 |
list_for_each_entry_safe(event, next_event, &client->event_list, link) kfree(event); |
19a15b937 firewire: Add dev... |
1662 |
|
fb4430367 firewire: cdev: r... |
1663 |
client_put(client); |
19a15b937 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 firewire: Use onl... |
1671 |
unsigned int mask = 0; |
19a15b937 firewire: Add dev... |
1672 1673 |
poll_wait(file, &client->wait, pt); |
2603bf219 firewire: Use onl... |
1674 1675 |
if (fw_device_is_shutdown(client->device)) mask |= POLLHUP | POLLERR; |
19a15b937 firewire: Add dev... |
1676 |
if (!list_empty(&client->event_list)) |
2603bf219 firewire: Use onl... |
1677 1678 1679 |
mask |= POLLIN | POLLRDNORM; return mask; |
19a15b937 firewire: Add dev... |
1680 |
} |
21ebcd122 firewire: mark so... |
1681 |
const struct file_operations fw_device_ops = { |
19a15b937 firewire: Add dev... |
1682 |
.owner = THIS_MODULE, |
3ac26b2ee firewire: cdev: m... |
1683 |
.llseek = no_llseek, |
19a15b937 firewire: Add dev... |
1684 1685 1686 |
.open = fw_device_op_open, .read = fw_device_op_read, .unlocked_ioctl = fw_device_op_ioctl, |
19a15b937 firewire: Add dev... |
1687 |
.mmap = fw_device_op_mmap, |
3ac26b2ee firewire: cdev: m... |
1688 1689 |
.release = fw_device_op_release, .poll = fw_device_op_poll, |
19a15b937 firewire: Add dev... |
1690 |
#ifdef CONFIG_COMPAT |
5af4e5eab firewire: comma a... |
1691 |
.compat_ioctl = fw_device_op_compat_ioctl, |
19a15b937 firewire: Add dev... |
1692 1693 |
#endif }; |