diff options
Diffstat (limited to 'drivers/firewire/core-cdev.c')
-rw-r--r-- | drivers/firewire/core-cdev.c | 73 |
1 files changed, 68 insertions, 5 deletions
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index f95719926487..0425dd5dfcd3 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c | |||
@@ -69,6 +69,9 @@ struct client { | |||
69 | struct fw_iso_buffer buffer; | 69 | struct fw_iso_buffer buffer; |
70 | unsigned long vm_start; | 70 | unsigned long vm_start; |
71 | 71 | ||
72 | struct list_head phy_receiver_link; | ||
73 | u64 phy_receiver_closure; | ||
74 | |||
72 | struct list_head link; | 75 | struct list_head link; |
73 | struct kref kref; | 76 | struct kref kref; |
74 | }; | 77 | }; |
@@ -201,6 +204,11 @@ struct outbound_phy_packet_event { | |||
201 | struct fw_cdev_event_phy_packet phy_packet; | 204 | struct fw_cdev_event_phy_packet phy_packet; |
202 | }; | 205 | }; |
203 | 206 | ||
207 | struct inbound_phy_packet_event { | ||
208 | struct event event; | ||
209 | struct fw_cdev_event_phy_packet phy_packet; | ||
210 | }; | ||
211 | |||
204 | static inline void __user *u64_to_uptr(__u64 value) | 212 | static inline void __user *u64_to_uptr(__u64 value) |
205 | { | 213 | { |
206 | return (void __user *)(unsigned long)value; | 214 | return (void __user *)(unsigned long)value; |
@@ -236,6 +244,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file) | |||
236 | idr_init(&client->resource_idr); | 244 | idr_init(&client->resource_idr); |
237 | INIT_LIST_HEAD(&client->event_list); | 245 | INIT_LIST_HEAD(&client->event_list); |
238 | init_waitqueue_head(&client->wait); | 246 | init_waitqueue_head(&client->wait); |
247 | INIT_LIST_HEAD(&client->phy_receiver_link); | ||
239 | kref_init(&client->kref); | 248 | kref_init(&client->kref); |
240 | 249 | ||
241 | file->private_data = client; | 250 | file->private_data = client; |
@@ -357,7 +366,7 @@ static void queue_bus_reset_event(struct client *client) | |||
357 | 366 | ||
358 | e = kzalloc(sizeof(*e), GFP_KERNEL); | 367 | e = kzalloc(sizeof(*e), GFP_KERNEL); |
359 | if (e == NULL) { | 368 | if (e == NULL) { |
360 | fw_notify("Out of memory when allocating bus reset event\n"); | 369 | fw_notify("Out of memory when allocating event\n"); |
361 | return; | 370 | return; |
362 | } | 371 | } |
363 | 372 | ||
@@ -404,6 +413,7 @@ union ioctl_arg { | |||
404 | struct fw_cdev_send_stream_packet send_stream_packet; | 413 | struct fw_cdev_send_stream_packet send_stream_packet; |
405 | struct fw_cdev_get_cycle_timer2 get_cycle_timer2; | 414 | struct fw_cdev_get_cycle_timer2 get_cycle_timer2; |
406 | struct fw_cdev_send_phy_packet send_phy_packet; | 415 | struct fw_cdev_send_phy_packet send_phy_packet; |
416 | struct fw_cdev_receive_phy_packets receive_phy_packets; | ||
407 | }; | 417 | }; |
408 | 418 | ||
409 | static int ioctl_get_info(struct client *client, union ioctl_arg *arg) | 419 | static int ioctl_get_info(struct client *client, union ioctl_arg *arg) |
@@ -671,9 +681,10 @@ static void handle_request(struct fw_card *card, struct fw_request *request, | |||
671 | 681 | ||
672 | r = kmalloc(sizeof(*r), GFP_ATOMIC); | 682 | r = kmalloc(sizeof(*r), GFP_ATOMIC); |
673 | e = kmalloc(sizeof(*e), GFP_ATOMIC); | 683 | e = kmalloc(sizeof(*e), GFP_ATOMIC); |
674 | if (r == NULL || e == NULL) | 684 | if (r == NULL || e == NULL) { |
685 | fw_notify("Out of memory when allocating event\n"); | ||
675 | goto failed; | 686 | goto failed; |
676 | 687 | } | |
677 | r->card = card; | 688 | r->card = card; |
678 | r->request = request; | 689 | r->request = request; |
679 | r->data = payload; | 690 | r->data = payload; |
@@ -902,9 +913,10 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle, | |||
902 | struct iso_interrupt_event *e; | 913 | struct iso_interrupt_event *e; |
903 | 914 | ||
904 | e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); | 915 | e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC); |
905 | if (e == NULL) | 916 | if (e == NULL) { |
917 | fw_notify("Out of memory when allocating event\n"); | ||
906 | return; | 918 | return; |
907 | 919 | } | |
908 | e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; | 920 | e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT; |
909 | e->interrupt.closure = client->iso_closure; | 921 | e->interrupt.closure = client->iso_closure; |
910 | e->interrupt.cycle = cycle; | 922 | e->interrupt.cycle = cycle; |
@@ -1447,6 +1459,52 @@ static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg) | |||
1447 | return 0; | 1459 | return 0; |
1448 | } | 1460 | } |
1449 | 1461 | ||
1462 | static int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg) | ||
1463 | { | ||
1464 | struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets; | ||
1465 | struct fw_card *card = client->device->card; | ||
1466 | |||
1467 | /* Access policy: Allow this ioctl only on local nodes' device files. */ | ||
1468 | if (!client->device->is_local) | ||
1469 | return -ENOSYS; | ||
1470 | |||
1471 | spin_lock_irq(&card->lock); | ||
1472 | |||
1473 | list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list); | ||
1474 | client->phy_receiver_closure = a->closure; | ||
1475 | |||
1476 | spin_unlock_irq(&card->lock); | ||
1477 | |||
1478 | return 0; | ||
1479 | } | ||
1480 | |||
1481 | void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p) | ||
1482 | { | ||
1483 | struct client *client; | ||
1484 | struct inbound_phy_packet_event *e; | ||
1485 | unsigned long flags; | ||
1486 | |||
1487 | spin_lock_irqsave(&card->lock, flags); | ||
1488 | |||
1489 | list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) { | ||
1490 | e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC); | ||
1491 | if (e == NULL) { | ||
1492 | fw_notify("Out of memory when allocating event\n"); | ||
1493 | break; | ||
1494 | } | ||
1495 | e->phy_packet.closure = client->phy_receiver_closure; | ||
1496 | e->phy_packet.type = FW_CDEV_EVENT_PHY_PACKET_RECEIVED; | ||
1497 | e->phy_packet.rcode = RCODE_COMPLETE; | ||
1498 | e->phy_packet.length = 8; | ||
1499 | e->phy_packet.data[0] = p->header[1]; | ||
1500 | e->phy_packet.data[1] = p->header[2]; | ||
1501 | queue_event(client, &e->event, | ||
1502 | &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0); | ||
1503 | } | ||
1504 | |||
1505 | spin_unlock_irqrestore(&card->lock, flags); | ||
1506 | } | ||
1507 | |||
1450 | static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { | 1508 | static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { |
1451 | [0x00] = ioctl_get_info, | 1509 | [0x00] = ioctl_get_info, |
1452 | [0x01] = ioctl_send_request, | 1510 | [0x01] = ioctl_send_request, |
@@ -1470,6 +1528,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = { | |||
1470 | [0x13] = ioctl_send_stream_packet, | 1528 | [0x13] = ioctl_send_stream_packet, |
1471 | [0x14] = ioctl_get_cycle_timer2, | 1529 | [0x14] = ioctl_get_cycle_timer2, |
1472 | [0x15] = ioctl_send_phy_packet, | 1530 | [0x15] = ioctl_send_phy_packet, |
1531 | [0x16] = ioctl_receive_phy_packets, | ||
1473 | }; | 1532 | }; |
1474 | 1533 | ||
1475 | static int dispatch_ioctl(struct client *client, | 1534 | static int dispatch_ioctl(struct client *client, |
@@ -1577,6 +1636,10 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
1577 | struct client *client = file->private_data; | 1636 | struct client *client = file->private_data; |
1578 | struct event *event, *next_event; | 1637 | struct event *event, *next_event; |
1579 | 1638 | ||
1639 | spin_lock_irq(&client->device->card->lock); | ||
1640 | list_del(&client->phy_receiver_link); | ||
1641 | spin_unlock_irq(&client->device->card->lock); | ||
1642 | |||
1580 | mutex_lock(&client->device->client_list_mutex); | 1643 | mutex_lock(&client->device->client_list_mutex); |
1581 | list_del(&client->link); | 1644 | list_del(&client->link); |
1582 | mutex_unlock(&client->device->client_list_mutex); | 1645 | mutex_unlock(&client->device->client_list_mutex); |