Merge firewire branches to be released post v2.6.35
[pandora-kernel.git] / drivers / firewire / core-cdev.c
index 14a34d9..14bb7b7 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>
@@ -33,7 +34,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/poll.h>
-#include <linux/sched.h>
+#include <linux/sched.h> /* required for linux/wait.h */
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/string.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
+#define FW_CDEV_VERSION_ALLOCATE_REGION_END    4
+
 struct client {
        u32 version;
        struct fw_device *device;
@@ -63,6 +71,9 @@ struct client {
        struct fw_iso_buffer buffer;
        unsigned long vm_start;
 
+       struct list_head phy_receiver_link;
+       u64 phy_receiver_closure;
+
        struct list_head link;
        struct kref kref;
 };
@@ -107,6 +118,7 @@ struct outbound_transaction_resource {
 
 struct inbound_transaction_resource {
        struct client_resource resource;
+       struct fw_card *card;
        struct fw_request *request;
        void *data;
        size_t length;
@@ -171,7 +183,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 {
@@ -179,11 +194,28 @@ struct iso_interrupt_event {
        struct fw_cdev_event_iso_interrupt interrupt;
 };
 
+struct iso_interrupt_mc_event {
+       struct event event;
+       struct fw_cdev_event_iso_interrupt_mc interrupt;
+};
+
 struct iso_resource_event {
        struct event event;
        struct fw_cdev_event_iso_resource iso_resource;
 };
 
+struct outbound_phy_packet_event {
+       struct event event;
+       struct client *client;
+       struct fw_packet p;
+       struct fw_cdev_event_phy_packet phy_packet;
+};
+
+struct inbound_phy_packet_event {
+       struct event event;
+       struct fw_cdev_event_phy_packet phy_packet;
+};
+
 static inline void __user *u64_to_uptr(__u64 value)
 {
        return (void __user *)(unsigned long)value;
@@ -219,6 +251,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
        idr_init(&client->resource_idr);
        INIT_LIST_HEAD(&client->event_list);
        init_waitqueue_head(&client->wait);
+       INIT_LIST_HEAD(&client->phy_receiver_link);
        kref_init(&client->kref);
 
        file->private_data = client;
@@ -227,7 +260,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file)
        list_add_tail(&client->link, &device->client_list);
        mutex_unlock(&device->client_list_mutex);
 
-       return 0;
+       return nonseekable_open(inode, file);
 }
 
 static void queue_event(struct client *client, struct event *event,
@@ -309,7 +342,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;
 
@@ -340,7 +373,7 @@ static void queue_bus_reset_event(struct client *client)
 
        e = kzalloc(sizeof(*e), GFP_KERNEL);
        if (e == NULL) {
-               fw_notify("Out of memory when allocating bus reset event\n");
+               fw_notify("Out of memory when allocating event\n");
                return;
        }
 
@@ -386,6 +419,9 @@ union ioctl_arg {
        struct fw_cdev_allocate_iso_resource    allocate_iso_resource;
        struct fw_cdev_send_stream_packet       send_stream_packet;
        struct fw_cdev_get_cycle_timer2         get_cycle_timer2;
+       struct fw_cdev_send_phy_packet          send_phy_packet;
+       struct fw_cdev_receive_phy_packets      receive_phy_packets;
+       struct fw_cdev_set_iso_channels         set_iso_channels;
 };
 
 static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
@@ -395,7 +431,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 +590,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;
@@ -626,28 +666,34 @@ static void release_request(struct client *client,
        if (is_fcp_request(r->request))
                kfree(r->data);
        else
-               fw_send_response(client->device->card, r->request,
-                                RCODE_CONFLICT_ERROR);
+               fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR);
+
+       fw_card_put(r->card);
        kfree(r);
 }
 
 static void handle_request(struct fw_card *card, struct fw_request *request,
                           int tcode, int destination, int source,
-                          int generation, int speed,
-                          unsigned long long offset,
+                          int generation, unsigned long long offset,
                           void *payload, size_t length, void *callback_data)
 {
        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)
+       if (r == NULL || e == NULL) {
+               fw_notify("Out of memory when allocating event\n");
                goto failed;
-
+       }
+       r->card    = card;
        r->request = request;
        r->data    = payload;
        r->length  = length;
@@ -669,15 +715,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:
@@ -687,6 +755,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,
@@ -711,7 +781,11 @@ static int ioctl_allocate(struct client *client, union ioctl_arg *arg)
                return -ENOMEM;
 
        region.start = a->offset;
-       region.end   = a->offset + a->length;
+       if (client->version < FW_CDEV_VERSION_ALLOCATE_REGION_END)
+               region.end = a->offset + a->length;
+       else
+               region.end = a->region_end;
+
        r->handler.length           = a->length;
        r->handler.address_callback = handle_request;
        r->handler.callback_data    = r;
@@ -723,6 +797,7 @@ static int ioctl_allocate(struct client *client, union ioctl_arg *arg)
                kfree(r);
                return ret;
        }
+       a->offset = r->handler.offset;
 
        r->resource.release = release_address_handler;
        ret = add_client_resource(client, &r->resource, GFP_KERNEL);
@@ -757,15 +832,19 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg)
        if (is_fcp_request(r->request))
                goto out;
 
-       if (a->length < r->length)
-               r->length = a->length;
-       if (copy_from_user(r->data, u64_to_uptr(a->data), r->length)) {
+       if (a->length != fw_get_response_length(r->request)) {
+               ret = -EINVAL;
+               kfree(r->request);
+               goto out;
+       }
+       if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) {
                ret = -EFAULT;
                kfree(r->request);
                goto out;
        }
-       fw_send_response(client->device->card, r->request, a->rcode);
+       fw_send_response(r->card, r->request, a->rcode);
  out:
+       fw_card_put(r->card);
        kfree(r);
 
        return ret;
@@ -773,8 +852,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,
@@ -845,10 +925,11 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle,
        struct client *client = data;
        struct iso_interrupt_event *e;
 
-       e = kzalloc(sizeof(*e) + header_length, GFP_ATOMIC);
-       if (e == NULL)
+       e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC);
+       if (e == NULL) {
+               fw_notify("Out of memory when allocating event\n");
                return;
-
+       }
        e->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT;
        e->interrupt.closure   = client->iso_closure;
        e->interrupt.cycle     = cycle;
@@ -858,27 +939,54 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle,
                    sizeof(e->interrupt) + header_length, NULL, 0);
 }
 
+static void iso_mc_callback(struct fw_iso_context *context,
+                           dma_addr_t completed, void *data)
+{
+       struct client *client = data;
+       struct iso_interrupt_mc_event *e;
+
+       e = kmalloc(sizeof(*e), GFP_ATOMIC);
+       if (e == NULL) {
+               fw_notify("Out of memory when allocating event\n");
+               return;
+       }
+       e->interrupt.type      = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL;
+       e->interrupt.closure   = client->iso_closure;
+       e->interrupt.completed = fw_iso_buffer_lookup(&client->buffer,
+                                                     completed);
+       queue_event(client, &e->event, &e->interrupt,
+                   sizeof(e->interrupt), NULL, 0);
+}
+
 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;
+       fw_iso_callback_t cb;
 
-       /* We only support one context at this time. */
-       if (client->iso_context != NULL)
-               return -EBUSY;
-
-       if (a->channel > 63)
-               return -EINVAL;
+       BUILD_BUG_ON(FW_CDEV_ISO_CONTEXT_TRANSMIT != FW_ISO_CONTEXT_TRANSMIT ||
+                    FW_CDEV_ISO_CONTEXT_RECEIVE  != FW_ISO_CONTEXT_RECEIVE  ||
+                    FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL !=
+                                       FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL);
 
        switch (a->type) {
-       case FW_ISO_CONTEXT_RECEIVE:
-               if (a->header_size < 4 || (a->header_size & 3))
+       case FW_ISO_CONTEXT_TRANSMIT:
+               if (a->speed > SCODE_3200 || a->channel > 63)
                        return -EINVAL;
+
+               cb = iso_callback;
                break;
 
-       case FW_ISO_CONTEXT_TRANSMIT:
-               if (a->speed > SCODE_3200)
+       case FW_ISO_CONTEXT_RECEIVE:
+               if (a->header_size < 4 || (a->header_size & 3) ||
+                   a->channel > 63)
                        return -EINVAL;
+
+               cb = iso_callback;
+               break;
+
+       case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
+               cb = (fw_iso_callback_t)iso_mc_callback;
                break;
 
        default:
@@ -886,20 +994,37 @@ static int ioctl_create_iso_context(struct client *client, union ioctl_arg *arg)
        }
 
        context = fw_iso_context_create(client->device->card, a->type,
-                                       a->channel, a->speed, a->header_size,
-                                       iso_callback, client);
+                       a->channel, a->speed, a->header_size, cb, client);
        if (IS_ERR(context))
                return PTR_ERR(context);
 
+       /* We only support one context at this time. */
+       spin_lock_irq(&client->lock);
+       if (client->iso_context != NULL) {
+               spin_unlock_irq(&client->lock);
+               fw_iso_context_destroy(context);
+               return -EBUSY;
+       }
        client->iso_closure = a->closure;
        client->iso_context = context;
+       spin_unlock_irq(&client->lock);
 
-       /* We only support one context at this time. */
        a->handle = 0;
 
        return 0;
 }
 
+static int ioctl_set_iso_channels(struct client *client, union ioctl_arg *arg)
+{
+       struct fw_cdev_set_iso_channels *a = &arg->set_iso_channels;
+       struct fw_iso_context *ctx = client->iso_context;
+
+       if (ctx == NULL || a->handle != 0)
+               return -EINVAL;
+
+       return fw_iso_context_set_channels(ctx, &a->channels);
+}
+
 /* Macros for decoding the iso packet control header. */
 #define GET_PAYLOAD_LENGTH(v)  ((v) & 0xffff)
 #define GET_INTERRUPT(v)       (((v) >> 16) & 0x01)
@@ -913,7 +1038,7 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
        struct fw_cdev_queue_iso *a = &arg->queue_iso;
        struct fw_cdev_iso_packet __user *p, *end, *next;
        struct fw_iso_context *ctx = client->iso_context;
-       unsigned long payload, buffer_end, header_length;
+       unsigned long payload, buffer_end, transmit_header_bytes = 0;
        u32 control;
        int count;
        struct {
@@ -933,7 +1058,6 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
         * use the indirect payload, the iso buffer need not be mapped
         * and the a->data pointer is ignored.
         */
-
        payload = (unsigned long)a->data - client->vm_start;
        buffer_end = client->buffer.page_count << PAGE_SHIFT;
        if (a->data == 0 || client->buffer.pages == NULL ||
@@ -942,8 +1066,10 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
                buffer_end = 0;
        }
 
-       p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets);
+       if (ctx->type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL && payload & 3)
+               return -EINVAL;
 
+       p = (struct fw_cdev_iso_packet __user *)u64_to_uptr(a->packets);
        if (!access_ok(VERIFY_READ, p, a->size))
                return -EFAULT;
 
@@ -959,31 +1085,32 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
                u.packet.sy = GET_SY(control);
                u.packet.header_length = GET_HEADER_LENGTH(control);
 
-               if (ctx->type == FW_ISO_CONTEXT_TRANSMIT) {
-                       if (u.packet.header_length % 4 != 0)
+               switch (ctx->type) {
+               case FW_ISO_CONTEXT_TRANSMIT:
+                       if (u.packet.header_length & 3)
                                return -EINVAL;
-                       header_length = u.packet.header_length;
-               } else {
-                       /*
-                        * We require that header_length is a multiple of
-                        * the fixed header size, ctx->header_size.
-                        */
-                       if (ctx->header_size == 0) {
-                               if (u.packet.header_length > 0)
-                                       return -EINVAL;
-                       } else if (u.packet.header_length == 0 ||
-                                  u.packet.header_length % ctx->header_size != 0) {
+                       transmit_header_bytes = u.packet.header_length;
+                       break;
+
+               case FW_ISO_CONTEXT_RECEIVE:
+                       if (u.packet.header_length == 0 ||
+                           u.packet.header_length % ctx->header_size != 0)
+                               return -EINVAL;
+                       break;
+
+               case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
+                       if (u.packet.payload_length == 0 ||
+                           u.packet.payload_length & 3)
                                return -EINVAL;
-                       }
-                       header_length = 0;
+                       break;
                }
 
                next = (struct fw_cdev_iso_packet __user *)
-                       &p->header[header_length / 4];
+                       &p->header[transmit_header_bytes / 4];
                if (next > end)
                        return -EINVAL;
                if (__copy_from_user
-                   (u.packet.header, p->header, header_length))
+                   (u.packet.header, p->header, transmit_header_bytes))
                        return -EFAULT;
                if (u.packet.skip && ctx->type == FW_ISO_CONTEXT_TRANSMIT &&
                    u.packet.header_length + u.packet.payload_length > 0)
@@ -1011,6 +1138,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;
 
@@ -1042,7 +1176,7 @@ static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg)
 
        local_irq_disable();
 
-       cycle_time = card->driver->get_cycle_time(card);
+       cycle_time = card->driver->read_csr(card, CSR_CYCLE_TIME);
 
        switch (a->clk_id) {
        case CLOCK_REALTIME:      getnstimeofday(&ts);                   break;
@@ -1323,28 +1457,135 @@ static int ioctl_send_stream_packet(struct client *client, union ioctl_arg *arg)
        return init_request(client, &request, dest, a->speed);
 }
 
+static void outbound_phy_packet_callback(struct fw_packet *packet,
+                                        struct fw_card *card, int status)
+{
+       struct outbound_phy_packet_event *e =
+               container_of(packet, struct outbound_phy_packet_event, p);
+
+       switch (status) {
+       /* expected: */
+       case ACK_COMPLETE:      e->phy_packet.rcode = RCODE_COMPLETE;   break;
+       /* should never happen with PHY packets: */
+       case ACK_PENDING:       e->phy_packet.rcode = RCODE_COMPLETE;   break;
+       case ACK_BUSY_X:
+       case ACK_BUSY_A:
+       case ACK_BUSY_B:        e->phy_packet.rcode = RCODE_BUSY;       break;
+       case ACK_DATA_ERROR:    e->phy_packet.rcode = RCODE_DATA_ERROR; break;
+       case ACK_TYPE_ERROR:    e->phy_packet.rcode = RCODE_TYPE_ERROR; break;
+       /* stale generation; cancelled; on certain controllers: no ack */
+       default:                e->phy_packet.rcode = status;           break;
+       }
+       e->phy_packet.data[0] = packet->timestamp;
+
+       queue_event(e->client, &e->event, &e->phy_packet,
+                   sizeof(e->phy_packet) + e->phy_packet.length, NULL, 0);
+       client_put(e->client);
+}
+
+static int ioctl_send_phy_packet(struct client *client, union ioctl_arg *arg)
+{
+       struct fw_cdev_send_phy_packet *a = &arg->send_phy_packet;
+       struct fw_card *card = client->device->card;
+       struct outbound_phy_packet_event *e;
+
+       /* Access policy: Allow this ioctl only on local nodes' device files. */
+       if (!client->device->is_local)
+               return -ENOSYS;
+
+       e = kzalloc(sizeof(*e) + 4, GFP_KERNEL);
+       if (e == NULL)
+               return -ENOMEM;
+
+       client_get(client);
+       e->client               = client;
+       e->p.speed              = SCODE_100;
+       e->p.generation         = a->generation;
+       e->p.header[0]          = a->data[0];
+       e->p.header[1]          = a->data[1];
+       e->p.header_length      = 8;
+       e->p.callback           = outbound_phy_packet_callback;
+       e->phy_packet.closure   = a->closure;
+       e->phy_packet.type      = FW_CDEV_EVENT_PHY_PACKET_SENT;
+       if (is_ping_packet(a->data))
+                       e->phy_packet.length = 4;
+
+       card->driver->send_request(card, &e->p);
+
+       return 0;
+}
+
+static int ioctl_receive_phy_packets(struct client *client, union ioctl_arg *arg)
+{
+       struct fw_cdev_receive_phy_packets *a = &arg->receive_phy_packets;
+       struct fw_card *card = client->device->card;
+
+       /* Access policy: Allow this ioctl only on local nodes' device files. */
+       if (!client->device->is_local)
+               return -ENOSYS;
+
+       spin_lock_irq(&card->lock);
+
+       list_move_tail(&client->phy_receiver_link, &card->phy_receiver_list);
+       client->phy_receiver_closure = a->closure;
+
+       spin_unlock_irq(&card->lock);
+
+       return 0;
+}
+
+void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p)
+{
+       struct client *client;
+       struct inbound_phy_packet_event *e;
+       unsigned long flags;
+
+       spin_lock_irqsave(&card->lock, flags);
+
+       list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) {
+               e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC);
+               if (e == NULL) {
+                       fw_notify("Out of memory when allocating event\n");
+                       break;
+               }
+               e->phy_packet.closure   = client->phy_receiver_closure;
+               e->phy_packet.type      = FW_CDEV_EVENT_PHY_PACKET_RECEIVED;
+               e->phy_packet.rcode     = RCODE_COMPLETE;
+               e->phy_packet.length    = 8;
+               e->phy_packet.data[0]   = p->header[1];
+               e->phy_packet.data[1]   = p->header[2];
+               queue_event(client, &e->event,
+                           &e->phy_packet, sizeof(e->phy_packet) + 8, NULL, 0);
+       }
+
+       spin_unlock_irqrestore(&card->lock, flags);
+}
+
 static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
-       ioctl_get_info,
-       ioctl_send_request,
-       ioctl_allocate,
-       ioctl_deallocate,
-       ioctl_send_response,
-       ioctl_initiate_bus_reset,
-       ioctl_add_descriptor,
-       ioctl_remove_descriptor,
-       ioctl_create_iso_context,
-       ioctl_queue_iso,
-       ioctl_start_iso,
-       ioctl_stop_iso,
-       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,
-       ioctl_get_cycle_timer2,
+       [0x00] = ioctl_get_info,
+       [0x01] = ioctl_send_request,
+       [0x02] = ioctl_allocate,
+       [0x03] = ioctl_deallocate,
+       [0x04] = ioctl_send_response,
+       [0x05] = ioctl_initiate_bus_reset,
+       [0x06] = ioctl_add_descriptor,
+       [0x07] = ioctl_remove_descriptor,
+       [0x08] = ioctl_create_iso_context,
+       [0x09] = ioctl_queue_iso,
+       [0x0a] = ioctl_start_iso,
+       [0x0b] = ioctl_stop_iso,
+       [0x0c] = ioctl_get_cycle_timer,
+       [0x0d] = ioctl_allocate_iso_resource,
+       [0x0e] = ioctl_deallocate_iso_resource,
+       [0x0f] = ioctl_allocate_iso_resource_once,
+       [0x10] = ioctl_deallocate_iso_resource_once,
+       [0x11] = ioctl_get_speed,
+       [0x12] = ioctl_send_broadcast_request,
+       [0x13] = ioctl_send_stream_packet,
+       [0x14] = ioctl_get_cycle_timer2,
+       [0x15] = ioctl_send_phy_packet,
+       [0x16] = ioctl_receive_phy_packets,
+       [0x17] = ioctl_set_iso_channels,
 };
 
 static int dispatch_ioctl(struct client *client,
@@ -1452,6 +1693,10 @@ static int fw_device_op_release(struct inode *inode, struct file *file)
        struct client *client = file->private_data;
        struct event *event, *next_event;
 
+       spin_lock_irq(&client->device->card->lock);
+       list_del(&client->phy_receiver_link);
+       spin_unlock_irq(&client->device->card->lock);
+
        mutex_lock(&client->device->client_list_mutex);
        list_del(&client->link);
        mutex_unlock(&client->device->client_list_mutex);
@@ -1496,13 +1741,13 @@ static unsigned int fw_device_op_poll(struct file *file, poll_table * pt)
 
 const struct file_operations fw_device_ops = {
        .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
        .open           = fw_device_op_open,
        .read           = fw_device_op_read,
        .unlocked_ioctl = fw_device_op_ioctl,
-       .poll           = fw_device_op_poll,
-       .release        = fw_device_op_release,
        .mmap           = fw_device_op_mmap,
-
+       .release        = fw_device_op_release,
+       .poll           = fw_device_op_poll,
 #ifdef CONFIG_COMPAT
        .compat_ioctl   = fw_device_op_compat_ioctl,
 #endif