firewire: core: integrate software-forced bus resets with bus management
[pandora-kernel.git] / drivers / firewire / core-cdev.c
index 8cbc2b8..ee2e873 100644 (file)
@@ -18,6 +18,7 @@
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+#include <linux/bug.h>
 #include <linux/compat.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 
 #include "core.h"
 
+/*
+ * ABI version history is documented in linux/firewire-cdev.h.
+ */
+#define FW_CDEV_KERNEL_VERSION         4
+#define FW_CDEV_VERSION_EVENT_REQUEST2 4
+
 struct client {
        u32 version;
        struct fw_device *device;
@@ -171,7 +178,10 @@ struct outbound_transaction_event {
 
 struct inbound_transaction_event {
        struct event event;
-       struct fw_cdev_event_request request;
+       union {
+               struct fw_cdev_event_request request;
+               struct fw_cdev_event_request2 request2;
+       } req;
 };
 
 struct iso_interrupt_event {
@@ -309,7 +319,7 @@ static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
        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->bm_node_id    = card->bm_node_id;
        event->irm_node_id   = card->irm_node->node_id;
        event->root_node_id  = card->root_node->node_id;
 
@@ -395,7 +405,7 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
        unsigned long ret = 0;
 
        client->version = a->version;
-       a->version = FW_CDEV_VERSION;
+       a->version = FW_CDEV_KERNEL_VERSION;
        a->card = client->device->card->index;
 
        down_read(&fw_device_rwsem);
@@ -554,6 +564,10 @@ static int init_request(struct client *client,
            (request->length > 4096 || request->length > 512 << speed))
                return -EIO;
 
+       if (request->tcode == TCODE_WRITE_QUADLET_REQUEST &&
+           request->length < 4)
+               return -EINVAL;
+
        e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
        if (e == NULL)
                return -ENOMEM;
@@ -627,6 +641,8 @@ static void release_request(struct client *client,
                kfree(r->data);
        else
                fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR);
+
+       fw_card_put(r->card);
        kfree(r);
 }
 
@@ -638,9 +654,13 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
        struct address_handler_resource *handler = callback_data;
        struct inbound_transaction_resource *r;
        struct inbound_transaction_event *e;
+       size_t event_size0;
        void *fcp_frame = NULL;
        int ret;
 
+       /* card may be different from handler->client->device->card */
+       fw_card_get(card);
+
        r = kmalloc(sizeof(*r), GFP_ATOMIC);
        e = kmalloc(sizeof(*e), GFP_ATOMIC);
        if (r == NULL || e == NULL)
@@ -668,15 +688,37 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
        if (ret < 0)
                goto failed;
 
-       e->request.type    = FW_CDEV_EVENT_REQUEST;
-       e->request.tcode   = tcode;
-       e->request.offset  = offset;
-       e->request.length  = length;
-       e->request.handle  = r->resource.handle;
-       e->request.closure = handler->closure;
+       if (handler->client->version < FW_CDEV_VERSION_EVENT_REQUEST2) {
+               struct fw_cdev_event_request *req = &e->req.request;
+
+               if (tcode & 0x10)
+                       tcode = TCODE_LOCK_REQUEST;
+
+               req->type       = FW_CDEV_EVENT_REQUEST;
+               req->tcode      = tcode;
+               req->offset     = offset;
+               req->length     = length;
+               req->handle     = r->resource.handle;
+               req->closure    = handler->closure;
+               event_size0     = sizeof(*req);
+       } else {
+               struct fw_cdev_event_request2 *req = &e->req.request2;
+
+               req->type       = FW_CDEV_EVENT_REQUEST2;
+               req->tcode      = tcode;
+               req->offset     = offset;
+               req->source_node_id = source;
+               req->destination_node_id = destination;
+               req->card       = card->index;
+               req->generation = generation;
+               req->length     = length;
+               req->handle     = r->resource.handle;
+               req->closure    = handler->closure;
+               event_size0     = sizeof(*req);
+       }
 
        queue_event(handler->client, &e->event,
-                   &e->request, sizeof(e->request), r->data, length);
+                   &e->req, event_size0, r->data, length);
        return;
 
  failed:
@@ -686,6 +728,8 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
 
        if (!is_fcp_request(request))
                fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+
+       fw_card_put(card);
 }
 
 static void release_address_handler(struct client *client,
@@ -768,6 +812,7 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg)
        }
        fw_send_response(r->card, r->request, a->rcode);
  out:
+       fw_card_put(r->card);
        kfree(r);
 
        return ret;
@@ -775,8 +820,9 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg)
 
 static int ioctl_initiate_bus_reset(struct client *client, union ioctl_arg *arg)
 {
-       return fw_core_initiate_bus_reset(client->device->card,
+       fw_schedule_bus_reset(client->device->card, true,
                        arg->initiate_bus_reset.type == FW_CDEV_SHORT_RESET);
+       return 0;
 }
 
 static void release_descriptor(struct client *client,
@@ -865,6 +911,9 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
        struct fw_cdev_create_iso_context *a = &arg->create_iso_context;
        struct fw_iso_context *context;
 
+       BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
+                    FW_CDEV_ISO_CONTEXT_RECEIVE  != FW_ISO_CONTEXT_RECEIVE);
+
        if (a->channel > 63)
                return -EINVAL;
 
@@ -1016,6 +1065,13 @@ static int ioctl_start_iso(struct client *client, union ioctl_arg *arg)
 {
        struct fw_cdev_start_iso *a = &arg->start_iso;
 
+       BUILD_BUG_ON(
+           FW_CDEV_ISO_CONTEXT_MATCH_TAG0 != FW_ISO_CONTEXT_MATCH_TAG0 ||
+           FW_CDEV_ISO_CONTEXT_MATCH_TAG1 != FW_ISO_CONTEXT_MATCH_TAG1 ||
+           FW_CDEV_ISO_CONTEXT_MATCH_TAG2 != FW_ISO_CONTEXT_MATCH_TAG2 ||
+           FW_CDEV_ISO_CONTEXT_MATCH_TAG3 != FW_ISO_CONTEXT_MATCH_TAG3 ||
+           FW_CDEV_ISO_CONTEXT_MATCH_ALL_TAGS != FW_ISO_CONTEXT_MATCH_ALL_TAGS);
+
        if (client->iso_context == NULL || a->handle != 0)
                return -EINVAL;