#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"
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;
+ if (fw_device_is_shutdown(device)) {
+ fw_device_put(device);
+ 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);
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
return dequeue_event(client, buffer, count);
}
+/* caller must hold card->lock so that node pointers can be dereferenced here */
static void
fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
struct client *client)
event->closure = client->bus_reset_closure;
event->type = FW_CDEV_EVENT_BUS_RESET;
+ event->generation = client->device->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
{
struct fw_cdev_get_info *get_info = buffer;
struct fw_cdev_event_bus_reset bus_reset;
+ struct fw_card *card = client->device->card;
+ unsigned long ret = 0;
client->version = get_info->version;
get_info->version = FW_CDEV_VERSION;
+ down_read(&fw_device_rwsem);
+
if (get_info->rom != 0) {
void __user *uptr = u64_to_uptr(get_info->rom);
size_t want = get_info->rom_length;
size_t have = client->device->config_rom_length * 4;
- if (copy_to_user(uptr, client->device->config_rom,
- min(want, have)))
- return -EFAULT;
+ ret = copy_to_user(uptr, client->device->config_rom,
+ min(want, have));
}
get_info->rom_length = client->device->config_rom_length * 4;
+ up_read(&fw_device_rwsem);
+
+ if (ret != 0)
+ return -EFAULT;
+
client->bus_reset_closure = get_info->bus_reset_closure;
if (get_info->bus_reset != 0) {
void __user *uptr = u64_to_uptr(get_info->bus_reset);
+ unsigned long flags;
+ spin_lock_irqsave(&card->lock, flags);
fill_bus_reset_event(&bus_reset, client);
+ spin_unlock_irqrestore(&card->lock, flags);
+
if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset)))
return -EFAULT;
}
- get_info->card = client->device->card->index;
+ get_info->card = card->index;
return 0;
}
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)
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;
{
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;
{
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,
ioctl_queue_iso,
ioctl_start_iso,
ioctl_stop_iso,
+ ioctl_get_cycle_timer,
};
static int
{
struct client *client = file->private_data;
+ if (fw_device_is_shutdown(client->device))
+ return -ENODEV;
+
return dispatch_ioctl(client, cmd, (void __user *) arg);
}
{
struct client *client = file->private_data;
+ if (fw_device_is_shutdown(client->device))
+ return -ENODEV;
+
return dispatch_ioctl(client, cmd, compat_ptr(arg));
}
#endif
unsigned long size;
int page_count, retval;
+ if (fw_device_is_shutdown(client->device))
+ return -ENODEV;
+
/* FIXME: We could support multiple buffers, but we don't. */
if (client->buffer.pages != NULL)
return -EBUSY;