Revert "drm: don't associate _DRM_DRIVER maps with a master"
[safe/jmp/linux-2.6] / drivers / firewire / fw-cdev.c
index a1637a8..7eb6594 100644 (file)
@@ -129,7 +129,7 @@ struct iso_resource {
        struct iso_resource_event *e_alloc, *e_dealloc;
 };
 
-static int schedule_iso_resource(struct iso_resource *);
+static void schedule_iso_resource(struct iso_resource *);
 static void release_iso_resource(struct client *, struct client_resource *);
 
 /*
@@ -237,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;
@@ -252,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++) {
@@ -286,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;
@@ -299,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,
@@ -432,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;
@@ -525,9 +522,9 @@ static int init_request(struct client *client,
        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)
@@ -553,15 +550,11 @@ static int init_request(struct client *client,
        client_get(client);
 
        fw_send_request(client->device->card, &e->r.transaction,
-                       request->tcode & 0x1f, destination_id,
-                       request->generation, speed, request->offset,
-                       e->response.data, request->length,
-                       complete_transaction, e);
+                       request->tcode, destination_id, request->generation,
+                       speed, request->offset, e->response.data,
+                       request->length, complete_transaction, e);
+       return 0;
 
-       if (request->data)
-               return sizeof(request) + request->length;
-       else
-               return sizeof(request);
  failed:
        kfree(e);
 
@@ -589,7 +582,7 @@ static int ioctl_send_request(struct client *client, void *buffer)
                return -EINVAL;
        }
 
-       return init_request(client, request, client->device->node->node_id,
+       return init_request(client, request, client->device->node_id,
                            client->device->max_speed);
 }
 
@@ -746,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;
 
@@ -1086,7 +1087,7 @@ 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;
@@ -1115,17 +1116,11 @@ static void iso_resource_work(struct work_struct *work)
        client_put(client);
 }
 
-static int schedule_iso_resource(struct iso_resource *r)
+static void schedule_iso_resource(struct iso_resource *r)
 {
-       int scheduled;
-
        client_get(r->client);
-
-       scheduled = schedule_delayed_work(&r->work, 0);
-       if (!scheduled)
+       if (!schedule_delayed_work(&r->work, 0))
                client_put(r->client);
-
-       return scheduled;
 }
 
 static void release_iso_resource(struct client *client,
@@ -1177,13 +1172,13 @@ static int init_iso_resource(struct client *client,
        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;
-               ret = schedule_iso_resource(r) ? 0 : -ENOMEM;
+               schedule_iso_resource(r);
        }
-       if (ret < 0)
-               goto fail;
        request->handle = r->resource.handle;
 
        return 0;
@@ -1224,13 +1219,14 @@ static int ioctl_deallocate_iso_resource_once(struct client *client, void *buffe
        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)
 {
-       struct fw_cdev_get_speed *request = buffer;
-
-       request->max_speed = client->device->max_speed;
-
-       return 0;
+       return client->device->max_speed;
 }
 
 static int ioctl_send_broadcast_request(struct client *client, void *buffer)
@@ -1245,9 +1241,36 @@ static int ioctl_send_broadcast_request(struct client *client, void *buffer)
                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,
@@ -1268,6 +1291,7 @@ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
        ioctl_deallocate_iso_resource_once,
        ioctl_get_speed,
        ioctl_send_broadcast_request,
+       ioctl_send_stream_packet,
 };
 
 static int dispatch_ioctl(struct client *client,
@@ -1381,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);