]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/firewire/fw-cdev.c
[MTD] cmdlineparts documentation change - explain where mtd-id comes from
[linux-2.6-omap-h63xx.git] / drivers / firewire / fw-cdev.c
index 4a541921a14a26e2a24c8c814b92f17035098de3..2e6d5848d2172011f94058b09c8634c56b091dea 100644 (file)
@@ -113,6 +113,11 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
        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) {
                fw_device_put(device);
@@ -200,6 +205,7 @@ fw_device_op_read(struct file *file,
        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)
@@ -209,7 +215,6 @@ 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. */
@@ -269,6 +274,7 @@ static int ioctl_get_info(struct client *client, void *buffer)
 {
        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;
@@ -294,13 +300,17 @@ static int ioctl_get_info(struct client *client, void *buffer)
        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;
 }
@@ -359,22 +369,33 @@ complete_transaction(struct fw_card *card, int rcode,
        struct response *response = data;
        struct client *client = response->client;
        unsigned long flags;
+       struct fw_cdev_event_response *r = &response->response;
 
-       if (length < response->response.length)
-               response->response.length = length;
+       if (length < r->length)
+               r->length = length;
        if (rcode == RCODE_COMPLETE)
-               memcpy(response->response.data, payload,
-                      response->response.length);
+               memcpy(r->data, payload, r->length);
 
        spin_lock_irqsave(&client->lock, flags);
        list_del(&response->resource.link);
        spin_unlock_irqrestore(&client->lock, flags);
 
-       response->response.type   = FW_CDEV_EVENT_RESPONSE;
-       response->response.rcode  = rcode;
-       queue_event(client, &response->event,
-                   &response->response, sizeof(response->response),
-                   response->response.data, response->response.length);
+       r->type   = FW_CDEV_EVENT_RESPONSE;
+       r->rcode  = rcode;
+
+       /*
+        * In the case that sizeof(*r) doesn't align with the position of the
+        * data, and the read is short, preserve an extra copy of the data
+        * to stay compatible with a pre-2.6.27 bug.  Since the bug is harmless
+        * for short reads and some apps depended on it, this is both safe
+        * and prudent for compatibility.
+        */
+       if (r->length <= sizeof(*r) - offsetof(typeof(*r), data))
+               queue_event(client, &response->event, r, sizeof(*r),
+                           r->data, r->length);
+       else
+               queue_event(client, &response->event, r, sizeof(*r) + r->length,
+                           NULL, 0);
 }
 
 static int ioctl_send_request(struct client *client, void *buffer)
@@ -901,6 +922,9 @@ fw_device_op_ioctl(struct file *file,
 {
        struct client *client = file->private_data;
 
+       if (fw_device_is_shutdown(client->device))
+               return -ENODEV;
+
        return dispatch_ioctl(client, cmd, (void __user *) arg);
 }
 
@@ -911,6 +935,9 @@ fw_device_op_compat_ioctl(struct file *file,
 {
        struct client *client = file->private_data;
 
+       if (fw_device_is_shutdown(client->device))
+               return -ENODEV;
+
        return dispatch_ioctl(client, cmd, compat_ptr(arg));
 }
 #endif
@@ -922,6 +949,9 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
        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;