]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/firewire/fw-cdev.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cooloney...
[linux-2.6-omap-h63xx.git] / drivers / firewire / fw-cdev.c
index 75388641a7d34619f1a1ec1c11aa856e5d13fe6f..46bc197a047fe84329f4ec868e351ae0438cac2e 100644 (file)
 #include <linux/device.h>
 #include <linux/vmalloc.h>
 #include <linux/poll.h>
+#include <linux/preempt.h>
+#include <linux/time.h>
 #include <linux/delay.h>
 #include <linux/mm.h>
 #include <linux/idr.h>
 #include <linux/compat.h>
 #include <linux/firewire-cdev.h>
+#include <asm/system.h>
 #include <asm/uaccess.h>
 #include "fw-transaction.h"
 #include "fw-topology.h"
@@ -106,15 +109,17 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
        struct client *client;
        unsigned long flags;
 
-       device = fw_device_from_devt(inode->i_rdev);
+       device = fw_device_get_by_devt(inode->i_rdev);
        if (device == NULL)
                return -ENODEV;
 
        client = kzalloc(sizeof(*client), GFP_KERNEL);
-       if (client == NULL)
+       if (client == NULL) {
+               fw_device_put(device);
                return -ENOMEM;
+       }
 
-       client->device = fw_device_get(device);
+       client->device = device;
        INIT_LIST_HEAD(&client->event_list);
        INIT_LIST_HEAD(&client->resource_list);
        spin_lock_init(&client->lock);
@@ -140,11 +145,10 @@ static void queue_event(struct client *client, struct event *event,
        event->v[1].size = size1;
 
        spin_lock_irqsave(&client->lock, flags);
-
        list_add_tail(&event->link, &client->event_list);
-       wake_up_interruptible(&client->wait);
-
        spin_unlock_irqrestore(&client->lock, flags);
+
+       wake_up_interruptible(&client->wait);
 }
 
 static int
@@ -204,12 +208,13 @@ fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
 
        event->closure       = client->bus_reset_closure;
        event->type          = FW_CDEV_EVENT_BUS_RESET;
+       event->generation    = client->device->generation;
+       smp_rmb();           /* node_id must not be older than generation */
        event->node_id       = client->device->node_id;
        event->local_node_id = card->local_node->node_id;
        event->bm_node_id    = 0; /* FIXME: We don't track the BM. */
        event->irm_node_id   = card->irm_node->node_id;
        event->root_node_id  = card->root_node->node_id;
-       event->generation    = card->generation;
 }
 
 static void
@@ -621,20 +626,19 @@ iso_callback(struct fw_iso_context *context, u32 cycle,
             size_t header_length, void *header, void *data)
 {
        struct client *client = data;
-       struct iso_interrupt *interrupt;
+       struct iso_interrupt *irq;
 
-       interrupt = kzalloc(sizeof(*interrupt) + header_length, GFP_ATOMIC);
-       if (interrupt == NULL)
+       irq = kzalloc(sizeof(*irq) + header_length, GFP_ATOMIC);
+       if (irq == NULL)
                return;
 
-       interrupt->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
-       interrupt->interrupt.closure   = client->iso_closure;
-       interrupt->interrupt.cycle     = cycle;
-       interrupt->interrupt.header_length = header_length;
-       memcpy(interrupt->interrupt.header, header, header_length);
-       queue_event(client, &interrupt->event,
-                   &interrupt->interrupt,
-                   sizeof(interrupt->interrupt) + header_length, NULL, 0);
+       irq->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
+       irq->interrupt.closure   = client->iso_closure;
+       irq->interrupt.cycle     = cycle;
+       irq->interrupt.header_length = header_length;
+       memcpy(irq->interrupt.header, header, header_length);
+       queue_event(client, &irq->event, &irq->interrupt,
+                   sizeof(irq->interrupt) + header_length, NULL, 0);
 }
 
 static int ioctl_create_iso_context(struct client *client, void *buffer)
@@ -642,6 +646,10 @@ static int ioctl_create_iso_context(struct client *client, void *buffer)
        struct fw_cdev_create_iso_context *request = buffer;
        struct fw_iso_context *context;
 
+       /* We only support one context at this time. */
+       if (client->iso_context != NULL)
+               return -EBUSY;
+
        if (request->channel > 63)
                return -EINVAL;
 
@@ -722,10 +730,11 @@ static int ioctl_queue_iso(struct client *client, void *buffer)
                buffer_end = 0;
        }
 
-       if (!access_ok(VERIFY_READ, request->packets, request->size))
+       p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets);
+
+       if (!access_ok(VERIFY_READ, p, request->size))
                return -EFAULT;
 
-       p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(request->packets);
        end = (void __user *)p + request->size;
        count = 0;
        while (p < end) {
@@ -787,8 +796,9 @@ static int ioctl_start_iso(struct client *client, void *buffer)
 {
        struct fw_cdev_start_iso *request = buffer;
 
-       if (request->handle != 0)
+       if (client->iso_context == NULL || request->handle != 0)
                return -EINVAL;
+
        if (client->iso_context->type == FW_ISO_CONTEXT_RECEIVE) {
                if (request->tags == 0 || request->tags > 15)
                        return -EINVAL;
@@ -805,12 +815,34 @@ static int ioctl_stop_iso(struct client *client, void *buffer)
 {
        struct fw_cdev_stop_iso *request = buffer;
 
-       if (request->handle != 0)
+       if (client->iso_context == NULL || request->handle != 0)
                return -EINVAL;
 
        return fw_iso_context_stop(client->iso_context);
 }
 
+static int ioctl_get_cycle_timer(struct client *client, void *buffer)
+{
+       struct fw_cdev_get_cycle_timer *request = buffer;
+       struct fw_card *card = client->device->card;
+       unsigned long long bus_time;
+       struct timeval tv;
+       unsigned long flags;
+
+       preempt_disable();
+       local_irq_save(flags);
+
+       bus_time = card->driver->get_bus_time(card);
+       do_gettimeofday(&tv);
+
+       local_irq_restore(flags);
+       preempt_enable();
+
+       request->local_time = tv.tv_sec * 1000000ULL + tv.tv_usec;
+       request->cycle_timer = bus_time & 0xffffffff;
+       return 0;
+}
+
 static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
        ioctl_get_info,
        ioctl_send_request,
@@ -824,6 +856,7 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
        ioctl_queue_iso,
        ioctl_start_iso,
        ioctl_stop_iso,
+       ioctl_get_cycle_timer,
 };
 
 static int