Revert "drm: don't associate _DRM_DRIVER maps with a master"
[safe/jmp/linux-2.6] / drivers / firewire / fw-cdev.c
index a227853..7eb6594 100644 (file)
@@ -121,7 +121,8 @@ struct iso_resource {
        struct client *client;
        /* Schedule work and access todo only with client->lock held. */
        struct delayed_work work;
-       enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,} todo;
+       enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
+             ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
        int generation;
        u64 channels;
        s32 bandwidth;
@@ -236,7 +237,6 @@ static void queue_event(struct client *client, struct event *event,
 static int dequeue_event(struct client *client,
                         char __user *buffer, size_t count)
 {
-       unsigned long flags;
        struct event *event;
        size_t size, total;
        int i, ret;
@@ -251,10 +251,10 @@ static int dequeue_event(struct client *client,
                       fw_device_is_shutdown(client->device))
                return -ENODEV;
 
-       spin_lock_irqsave(&client->lock, flags);
+       spin_lock_irq(&client->lock);
        event = list_first_entry(&client->event_list, struct event, link);
        list_del(&event->link);
-       spin_unlock_irqrestore(&client->lock, flags);
+       spin_unlock_irq(&client->lock);
 
        total = 0;
        for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
@@ -285,9 +285,8 @@ static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
                                 struct client *client)
 {
        struct fw_card *card = client->device->card;
-       unsigned long flags;
 
-       spin_lock_irqsave(&card->lock, flags);
+       spin_lock_irq(&card->lock);
 
        event->closure       = client->bus_reset_closure;
        event->type          = FW_CDEV_EVENT_BUS_RESET;
@@ -298,7 +297,7 @@ static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
        event->irm_node_id   = card->irm_node->node_id;
        event->root_node_id  = card->root_node->node_id;
 
-       spin_unlock_irqrestore(&card->lock, flags);
+       spin_unlock_irq(&card->lock);
 }
 
 static void for_each_client(struct fw_device *device,
@@ -431,16 +430,15 @@ static int release_client_resource(struct client *client, u32 handle,
                                   struct client_resource **resource)
 {
        struct client_resource *r;
-       unsigned long flags;
 
-       spin_lock_irqsave(&client->lock, flags);
+       spin_lock_irq(&client->lock);
        if (client->in_shutdown)
                r = NULL;
        else
                r = idr_find(&client->resource_idr, handle);
        if (r && r->release == release)
                idr_remove(&client->resource_idr, handle);
-       spin_unlock_irqrestore(&client->lock, flags);
+       spin_unlock_irq(&client->lock);
 
        if (!(r && r->release == release))
                return -EINVAL;
@@ -517,16 +515,16 @@ static void complete_transaction(struct fw_card *card, int rcode,
        client_put(client);
 }
 
-static int ioctl_send_request(struct client *client, void *buffer)
+static int init_request(struct client *client,
+                       struct fw_cdev_send_request *request,
+                       int destination_id, int speed)
 {
-       struct fw_device *device = client->device;
-       struct fw_cdev_send_request *request = buffer;
        struct outbound_transaction_event *e;
        int ret;
 
-       /* What is the biggest size we'll accept, really? */
-       if (request->length > 4096)
-               return -EINVAL;
+       if (request->tcode != TCODE_STREAM_DATA &&
+           (request->length > 4096 || request->length > 512 << speed))
+               return -EIO;
 
        e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
        if (e == NULL)
@@ -543,6 +541,30 @@ static int ioctl_send_request(struct client *client, void *buffer)
                goto failed;
        }
 
+       e->r.resource.release = release_transaction;
+       ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
+       if (ret < 0)
+               goto failed;
+
+       /* Get a reference for the transaction callback */
+       client_get(client);
+
+       fw_send_request(client->device->card, &e->r.transaction,
+                       request->tcode, destination_id, request->generation,
+                       speed, request->offset, e->response.data,
+                       request->length, complete_transaction, e);
+       return 0;
+
+ failed:
+       kfree(e);
+
+       return ret;
+}
+
+static int ioctl_send_request(struct client *client, void *buffer)
+{
+       struct fw_cdev_send_request *request = buffer;
+
        switch (request->tcode) {
        case TCODE_WRITE_QUADLET_REQUEST:
        case TCODE_WRITE_BLOCK_REQUEST:
@@ -557,35 +579,11 @@ static int ioctl_send_request(struct client *client, void *buffer)
        case TCODE_LOCK_VENDOR_DEPENDENT:
                break;
        default:
-               ret = -EINVAL;
-               goto failed;
+               return -EINVAL;
        }
 
-       e->r.resource.release = release_transaction;
-       ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
-       if (ret < 0)
-               goto failed;
-
-       /* Get a reference for the transaction callback */
-       client_get(client);
-
-       fw_send_request(device->card, &e->r.transaction,
-                       request->tcode & 0x1f,
-                       device->node->node_id,
-                       request->generation,
-                       device->max_speed,
-                       request->offset,
-                       e->response.data, request->length,
-                       complete_transaction, e);
-
-       if (request->data)
-               return sizeof(request) + request->length;
-       else
-               return sizeof(request);
- failed:
-       kfree(e);
-
-       return ret;
+       return init_request(client, request, client->device->node_id,
+                           client->device->max_speed);
 }
 
 static void release_request(struct client *client,
@@ -741,9 +739,17 @@ static void release_descriptor(struct client *client,
 static int ioctl_add_descriptor(struct client *client, void *buffer)
 {
        struct fw_cdev_add_descriptor *request = buffer;
+       struct fw_card *card = client->device->card;
        struct descriptor_resource *r;
        int ret;
 
+       /* Access policy: Allow this ioctl only on local nodes' device files. */
+       spin_lock_irq(&card->lock);
+       ret = client->device->node_id != card->local_node->node_id;
+       spin_unlock_irq(&card->lock);
+       if (ret)
+               return -ENOSYS;
+
        if (request->length > 256)
                return -EINVAL;
 
@@ -1033,7 +1039,9 @@ static void iso_resource_work(struct work_struct *work)
                skip = todo == ISO_RES_REALLOC &&
                       r->generation == generation;
        }
-       free = todo == ISO_RES_DEALLOC;
+       free = todo == ISO_RES_DEALLOC ||
+              todo == ISO_RES_ALLOC_ONCE ||
+              todo == ISO_RES_DEALLOC_ONCE;
        r->generation = generation;
        spin_unlock_irq(&client->lock);
 
@@ -1044,7 +1052,9 @@ static void iso_resource_work(struct work_struct *work)
 
        fw_iso_resource_manage(client->device->card, generation,
                        r->channels, &channel, &bandwidth,
-                       todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC);
+                       todo == ISO_RES_ALLOC ||
+                       todo == ISO_RES_REALLOC ||
+                       todo == ISO_RES_ALLOC_ONCE);
        /*
         * 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
@@ -1077,12 +1087,12 @@ static void iso_resource_work(struct work_struct *work)
        spin_unlock_irq(&client->lock);
 
        if (todo == ISO_RES_ALLOC && channel >= 0)
-               r->channels = 1ULL << (63 - channel);
+               r->channels = 1ULL << channel;
 
        if (todo == ISO_RES_REALLOC && success)
                goto out;
 
-       if (todo == ISO_RES_ALLOC) {
+       if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
                e = r->e_alloc;
                r->e_alloc = NULL;
        } else {
@@ -1108,8 +1118,9 @@ static void iso_resource_work(struct work_struct *work)
 
 static void schedule_iso_resource(struct iso_resource *r)
 {
-       if (schedule_delayed_work(&r->work, 0))
-               client_get(r->client);
+       client_get(r->client);
+       if (!schedule_delayed_work(&r->work, 0))
+               client_put(r->client);
 }
 
 static void release_iso_resource(struct client *client,
@@ -1124,9 +1135,9 @@ static void release_iso_resource(struct client *client,
        spin_unlock_irq(&client->lock);
 }
 
-static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
+static int init_iso_resource(struct client *client,
+               struct fw_cdev_allocate_iso_resource *request, int todo)
 {
-       struct fw_cdev_allocate_iso_resource *request = buffer;
        struct iso_resource_event *e1, *e2;
        struct iso_resource *r;
        int ret;
@@ -1146,7 +1157,7 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
 
        INIT_DELAYED_WORK(&r->work, iso_resource_work);
        r->client       = client;
-       r->todo         = ISO_RES_ALLOC;
+       r->todo         = todo;
        r->generation   = -1;
        r->channels     = request->channels;
        r->bandwidth    = request->bandwidth;
@@ -1158,10 +1169,16 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
        e2->resource.closure    = request->closure;
        e2->resource.type       = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
 
-       r->resource.release = release_iso_resource;
-       ret = add_client_resource(client, &r->resource, GFP_KERNEL);
-       if (ret < 0)
-               goto fail;
+       if (todo == ISO_RES_ALLOC) {
+               r->resource.release = release_iso_resource;
+               ret = add_client_resource(client, &r->resource, GFP_KERNEL);
+               if (ret < 0)
+                       goto fail;
+       } else {
+               r->resource.release = NULL;
+               r->resource.handle = -1;
+               schedule_iso_resource(r);
+       }
        request->handle = r->resource.handle;
 
        return 0;
@@ -1173,6 +1190,13 @@ static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
        return ret;
 }
 
+static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
+{
+       struct fw_cdev_allocate_iso_resource *request = buffer;
+
+       return init_iso_resource(client, request, ISO_RES_ALLOC);
+}
+
 static int ioctl_deallocate_iso_resource(struct client *client, void *buffer)
 {
        struct fw_cdev_deallocate *request = buffer;
@@ -1181,6 +1205,72 @@ static int ioctl_deallocate_iso_resource(struct client *client, void *buffer)
                                       release_iso_resource, NULL);
 }
 
+static int ioctl_allocate_iso_resource_once(struct client *client, void *buffer)
+{
+       struct fw_cdev_allocate_iso_resource *request = buffer;
+
+       return init_iso_resource(client, request, ISO_RES_ALLOC_ONCE);
+}
+
+static int ioctl_deallocate_iso_resource_once(struct client *client, void *buffer)
+{
+       struct fw_cdev_allocate_iso_resource *request = buffer;
+
+       return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE);
+}
+
+/*
+ * 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.
+ */
+static int ioctl_get_speed(struct client *client, void *buffer)
+{
+       return client->device->max_speed;
+}
+
+static int ioctl_send_broadcast_request(struct client *client, void *buffer)
+{
+       struct fw_cdev_send_request *request = buffer;
+
+       switch (request->tcode) {
+       case TCODE_WRITE_QUADLET_REQUEST:
+       case TCODE_WRITE_BLOCK_REQUEST:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Security policy: Only allow accesses to Units Space. */
+       if (request->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END)
+               return -EACCES;
+
+       return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100);
+}
+
+static int ioctl_send_stream_packet(struct client *client, void *buffer)
+{
+       struct fw_cdev_send_stream_packet *p = buffer;
+       struct fw_cdev_send_request request;
+       int dest;
+
+       if (p->speed > client->device->card->link_speed ||
+           p->length > 1024 << p->speed)
+               return -EIO;
+
+       if (p->tag > 3 || p->channel > 63 || p->sy > 15)
+               return -EINVAL;
+
+       dest = fw_stream_packet_destination_id(p->tag, p->channel, p->sy);
+       request.tcode           = TCODE_STREAM_DATA;
+       request.length          = p->length;
+       request.closure         = p->closure;
+       request.data            = p->data;
+       request.generation      = p->generation;
+
+       return init_request(client, &request, dest, p->speed);
+}
+
 static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
        ioctl_get_info,
        ioctl_send_request,
@@ -1197,6 +1287,11 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
        ioctl_get_cycle_timer,
        ioctl_allocate_iso_resource,
        ioctl_deallocate_iso_resource,
+       ioctl_allocate_iso_resource_once,
+       ioctl_deallocate_iso_resource_once,
+       ioctl_get_speed,
+       ioctl_send_broadcast_request,
+       ioctl_send_stream_packet,
 };
 
 static int dispatch_ioctl(struct client *client,
@@ -1310,22 +1405,21 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
 {
        struct client *client = file->private_data;
        struct event *e, *next_e;
-       unsigned long flags;
 
        mutex_lock(&client->device->client_list_mutex);
        list_del(&client->link);
        mutex_unlock(&client->device->client_list_mutex);
 
-       if (client->buffer.pages)
-               fw_iso_buffer_destroy(&client->buffer, client->device->card);
-
        if (client->iso_context)
                fw_iso_context_destroy(client->iso_context);
 
+       if (client->buffer.pages)
+               fw_iso_buffer_destroy(&client->buffer, client->device->card);
+
        /* Freeze client->resource_idr and client->event_list */
-       spin_lock_irqsave(&client->lock, flags);
+       spin_lock_irq(&client->lock);
        client->in_shutdown = true;
-       spin_unlock_irqrestore(&client->lock, flags);
+       spin_unlock_irq(&client->lock);
 
        idr_for_each(&client->resource_idr, shutdown_resource, client);
        idr_remove_all(&client->resource_idr);