firewire: Add ioctls to add and remove config rom descriptors.
authorKristian Høgsberg <krh@redhat.com>
Wed, 28 Mar 2007 19:26:42 +0000 (21:26 +0200)
committerStefan Richter <stefanr@s5r6.in-berlin.de>
Wed, 28 Mar 2007 19:30:14 +0000 (21:30 +0200)
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de> (fixed whitespace)
drivers/firewire/fw-card.c
drivers/firewire/fw-device-cdev.c
drivers/firewire/fw-device-cdev.h

index 34863b60e23fa764b281ddfbf1a8049b135f0eda..3eb06556a0c1f0ee70666dbfc622a11756772ebf 100644 (file)
@@ -160,7 +160,7 @@ fw_core_add_descriptor (struct fw_descriptor *desc)
                i += (desc->data[i] >> 16) + 1;
 
        if (i != desc->length)
-               return -1;
+               return -EINVAL;
 
        down_write(&fw_bus_type.subsys.rwsem);
 
index 12471444f1bd9feea4a5c112912d3b63bf98458c..e3c4a52a44a90a5b728560971a9925bca2711fd2 100644 (file)
@@ -73,9 +73,11 @@ struct client {
        u32 version;
        struct fw_device *device;
        spinlock_t lock;
+       u32 resource_handle;
        struct list_head handler_list;
        struct list_head request_list;
        struct list_head transaction_list;
+       struct list_head descriptor_list;
        u32 request_serial;
        struct list_head event_list;
        wait_queue_head_t wait;
@@ -119,6 +121,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
        INIT_LIST_HEAD(&client->handler_list);
        INIT_LIST_HEAD(&client->request_list);
        INIT_LIST_HEAD(&client->transaction_list);
+       INIT_LIST_HEAD(&client->descriptor_list);
        spin_lock_init(&client->lock);
        init_waitqueue_head(&client->wait);
 
@@ -542,6 +545,87 @@ static int ioctl_initiate_bus_reset(struct client *client, void __user *arg)
        return fw_core_initiate_bus_reset(client->device->card, short_reset);
 }
 
+struct descriptor {
+       struct fw_descriptor d;
+       struct list_head link;
+       u32 handle;
+       u32 data[0];
+};
+
+static int ioctl_add_descriptor(struct client *client, void __user *arg)
+{
+       struct fw_cdev_add_descriptor request;
+       struct descriptor *descriptor;
+       unsigned long flags;
+       int retval;
+
+       if (copy_from_user(&request, arg, sizeof request))
+               return -EFAULT;
+
+       if (request.length > 256)
+               return -EINVAL;
+
+       descriptor =
+               kmalloc(sizeof *descriptor + request.length * 4, GFP_KERNEL);
+       if (descriptor == NULL)
+               return -ENOMEM;
+
+       if (copy_from_user(descriptor->data,
+                          u64_to_uptr(request.data), request.length * 4)) {
+               kfree(descriptor);
+               return -EFAULT;
+       }
+
+       descriptor->d.length = request.length;
+       descriptor->d.immediate = request.immediate;
+       descriptor->d.key = request.key;
+       descriptor->d.data = descriptor->data;
+
+       retval = fw_core_add_descriptor(&descriptor->d);
+       if (retval < 0) {
+               kfree(descriptor);
+               return retval;
+       }
+
+       spin_lock_irqsave(&client->lock, flags);
+       list_add_tail(&descriptor->link, &client->descriptor_list);
+       descriptor->handle = client->resource_handle++;
+       spin_unlock_irqrestore(&client->lock, flags);
+
+       request.handle = descriptor->handle;
+       if (copy_to_user(arg, &request, sizeof request))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int ioctl_remove_descriptor(struct client *client, void __user *arg)
+{
+       struct fw_cdev_remove_descriptor request;
+       struct descriptor *d;
+       unsigned long flags;
+
+       if (copy_from_user(&request, arg, sizeof request))
+               return -EFAULT;
+
+       spin_lock_irqsave(&client->lock, flags);
+       list_for_each_entry(d, &client->descriptor_list, link) {
+               if (d->handle == request.handle) {
+                       list_del(&d->link);
+                       break;
+               }
+       }
+       spin_unlock_irqrestore(&client->lock, flags);
+
+       if (&d->link == &client->descriptor_list)
+               return -EINVAL;
+
+       fw_core_remove_descriptor(&d->d);
+       kfree(d);
+
+       return 0;
+}
+
 static void
 iso_callback(struct fw_iso_context *context, u32 cycle,
             size_t header_length, void *header, void *data)
@@ -731,6 +815,10 @@ dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
                return ioctl_send_response(client, arg);
        case FW_CDEV_IOC_INITIATE_BUS_RESET:
                return ioctl_initiate_bus_reset(client, arg);
+       case FW_CDEV_IOC_ADD_DESCRIPTOR:
+               return ioctl_add_descriptor(client, arg);
+       case FW_CDEV_IOC_REMOVE_DESCRIPTOR:
+               return ioctl_remove_descriptor(client, arg);
        case FW_CDEV_IOC_CREATE_ISO_CONTEXT:
                return ioctl_create_iso_context(client, arg);
        case FW_CDEV_IOC_QUEUE_ISO:
@@ -811,6 +899,7 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
        struct request *r, *next_r;
        struct event *e, *next_e;
        struct response *t, *next_t;
+       struct descriptor *d, *next_d;
        unsigned long flags;
 
        if (client->buffer.pages)
@@ -835,6 +924,11 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
                kfree(t);
        }
 
+       list_for_each_entry_safe(d, next_d, &client->descriptor_list, link) {
+               fw_core_remove_descriptor(&d->d);
+               kfree(d);
+       }
+
        /* FIXME: We should wait for the async tasklets to stop
         * running before freeing the memory. */
 
index 72befda989ba82210844eaa9203d38d87a9f09d5..62f5f66ca101d3db40db50ee15f4fd7ff3a5c27b 100644 (file)
@@ -130,10 +130,13 @@ union fw_cdev_event {
 #define FW_CDEV_IOC_DEALLOCATE         _IO('#', 0x03)
 #define FW_CDEV_IOC_SEND_RESPONSE      _IO('#', 0x04)
 #define FW_CDEV_IOC_INITIATE_BUS_RESET _IO('#', 0x05)
-#define FW_CDEV_IOC_CREATE_ISO_CONTEXT _IO('#', 0x06)
-#define FW_CDEV_IOC_QUEUE_ISO          _IO('#', 0x07)
-#define FW_CDEV_IOC_START_ISO          _IO('#', 0x08)
-#define FW_CDEV_IOC_STOP_ISO           _IO('#', 0x09)
+#define FW_CDEV_IOC_ADD_DESCRIPTOR     _IO('#', 0x06)
+#define FW_CDEV_IOC_REMOVE_DESCRIPTOR  _IO('#', 0x07)
+
+#define FW_CDEV_IOC_CREATE_ISO_CONTEXT _IO('#', 0x08)
+#define FW_CDEV_IOC_QUEUE_ISO          _IO('#', 0x09)
+#define FW_CDEV_IOC_START_ISO          _IO('#', 0x0a)
+#define FW_CDEV_IOC_STOP_ISO           _IO('#', 0x0b)
 
 /* FW_CDEV_VERSION History
  *
@@ -203,6 +206,18 @@ struct fw_cdev_initiate_bus_reset {
        __u32 type;
 };
 
+struct fw_cdev_add_descriptor {
+       __u32 immediate;
+       __u32 key;
+       __u64 data;
+       __u32 length;
+       __u32 handle;
+};
+
+struct fw_cdev_remove_descriptor {
+       __u32 handle;
+};
+
 #define FW_CDEV_ISO_CONTEXT_TRANSMIT   0
 #define FW_CDEV_ISO_CONTEXT_RECEIVE    1