#include <linux/list.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/compat.h>
 #include <soc/bcm2835/raspberrypi-firmware.h>
 
 #include "vchiq_core.h"
        return ret;
 }
 
+#if defined(CONFIG_COMPAT)
+
+struct vchiq_service_params32 {
+       int fourcc;
+       compat_uptr_t callback;
+       compat_uptr_t userdata;
+       short version; /* Increment for non-trivial changes */
+       short version_min; /* Update for incompatible changes */
+};
+
+struct vchiq_create_service32 {
+       struct vchiq_service_params32 params;
+       int is_open;
+       int is_vchi;
+       unsigned int handle; /* OUT */
+};
+
+#define VCHIQ_IOC_CREATE_SERVICE32 \
+       _IOWR(VCHIQ_IOC_MAGIC, 2, struct vchiq_create_service32)
+
+static long
+vchiq_compat_ioctl_create_service(
+       struct file *file,
+       unsigned int cmd,
+       unsigned long arg)
+{
+       VCHIQ_CREATE_SERVICE_T __user *args;
+       struct vchiq_create_service32 __user *ptrargs32 =
+               (struct vchiq_create_service32 __user *)arg;
+       struct vchiq_create_service32 args32;
+       long ret;
+
+       args = compat_alloc_user_space(sizeof(*args));
+       if (!args)
+               return -EFAULT;
+
+       if (copy_from_user(&args32,
+                          (struct vchiq_create_service32 __user *)arg,
+                          sizeof(args32)))
+               return -EFAULT;
+
+       if (put_user(args32.params.fourcc, &args->params.fourcc) ||
+           put_user(compat_ptr(args32.params.callback),
+                    &args->params.callback) ||
+           put_user(compat_ptr(args32.params.userdata),
+                    &args->params.userdata) ||
+           put_user(args32.params.version, &args->params.version) ||
+           put_user(args32.params.version_min,
+                    &args->params.version_min) ||
+           put_user(args32.is_open, &args->is_open) ||
+           put_user(args32.is_vchi, &args->is_vchi) ||
+           put_user(args32.handle, &args->handle))
+               return -EFAULT;
+
+       ret = vchiq_ioctl(file, VCHIQ_IOC_CREATE_SERVICE, (unsigned long)args);
+
+       if (ret < 0)
+               return ret;
+
+       if (get_user(args32.handle, &args->handle))
+               return -EFAULT;
+
+       if (copy_to_user(&ptrargs32->handle,
+                        &args32.handle,
+                        sizeof(args32.handle)))
+               return -EFAULT;
+
+       return 0;
+}
+
+struct vchiq_element32 {
+       compat_uptr_t data;
+       unsigned int size;
+};
+
+struct vchiq_queue_message32 {
+       unsigned int handle;
+       unsigned int count;
+       compat_uptr_t elements;
+};
+
+#define VCHIQ_IOC_QUEUE_MESSAGE32 \
+       _IOW(VCHIQ_IOC_MAGIC,  4, struct vchiq_queue_message32)
+
+static long
+vchiq_compat_ioctl_queue_message(struct file *file,
+                                unsigned int cmd,
+                                unsigned long arg)
+{
+       VCHIQ_QUEUE_MESSAGE_T *args;
+       VCHIQ_ELEMENT_T *elements;
+       struct vchiq_queue_message32 args32;
+       unsigned int count;
+
+       if (copy_from_user(&args32,
+                          (struct vchiq_queue_message32 __user *)arg,
+                          sizeof(args32)))
+               return -EFAULT;
+
+       args = compat_alloc_user_space(sizeof(*args) +
+                                      (sizeof(*elements) * MAX_ELEMENTS));
+
+       if (!args)
+               return -EFAULT;
+
+       if (put_user(args32.handle, &args->handle) ||
+           put_user(args32.count, &args->count) ||
+           put_user(compat_ptr(args32.elements), &args->elements))
+               return -EFAULT;
+
+       if (args32.count > MAX_ELEMENTS)
+               return -EINVAL;
+
+       if (args32.elements && args32.count) {
+               struct vchiq_element32 tempelement32[MAX_ELEMENTS];
+
+               elements = (VCHIQ_ELEMENT_T __user *)(args + 1);
+
+               if (copy_from_user(&tempelement32,
+                                  compat_ptr(args32.elements),
+                                  sizeof(tempelement32)))
+                       return -EFAULT;
+
+               for (count = 0; count < args32.count; count++) {
+                       if (put_user(compat_ptr(tempelement32[count].data),
+                                    &elements[count].data) ||
+                           put_user(tempelement32[count].size,
+                                    &elements[count].size))
+                               return -EFAULT;
+               }
+
+               if (put_user(elements, &args->elements))
+                       return -EFAULT;
+       }
+
+       return vchiq_ioctl(file, VCHIQ_IOC_QUEUE_MESSAGE, (unsigned long)args);
+}
+
+struct vchiq_queue_bulk_transfer32 {
+       unsigned int handle;
+       compat_uptr_t data;
+       unsigned int size;
+       compat_uptr_t userdata;
+       VCHIQ_BULK_MODE_T mode;
+};
+
+#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT32 \
+       _IOWR(VCHIQ_IOC_MAGIC, 5, struct vchiq_queue_bulk_transfer32)
+#define VCHIQ_IOC_QUEUE_BULK_RECEIVE32 \
+       _IOWR(VCHIQ_IOC_MAGIC, 6, struct vchiq_queue_bulk_transfer32)
+
+static long
+vchiq_compat_ioctl_queue_bulk(struct file *file,
+                             unsigned int cmd,
+                             unsigned long arg)
+{
+       VCHIQ_QUEUE_BULK_TRANSFER_T *args;
+       struct vchiq_queue_bulk_transfer32 args32;
+       struct vchiq_queue_bulk_transfer32 *ptrargs32 =
+               (struct vchiq_queue_bulk_transfer32 *)arg;
+       long ret;
+
+       args = compat_alloc_user_space(sizeof(*args));
+       if (!args)
+               return -EFAULT;
+
+       if (copy_from_user(&args32,
+                          (struct vchiq_queue_bulk_transfer32 __user *)arg,
+                          sizeof(args32)))
+               return -EFAULT;
+
+       if (put_user(args32.handle, &args->handle) ||
+           put_user(compat_ptr(args32.data), &args->data) ||
+           put_user(args32.size, &args->size) ||
+           put_user(compat_ptr(args32.userdata), &args->userdata) ||
+           put_user(args32.mode, &args->mode))
+               return -EFAULT;
+
+       if (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT32)
+               cmd = VCHIQ_IOC_QUEUE_BULK_TRANSMIT;
+       else
+               cmd = VCHIQ_IOC_QUEUE_BULK_RECEIVE;
+
+       ret = vchiq_ioctl(file, cmd, (unsigned long)args);
+
+       if (ret < 0)
+               return ret;
+
+       if (get_user(args32.mode, &args->mode))
+               return -EFAULT;
+
+       if (copy_to_user(&ptrargs32->mode,
+                        &args32.mode,
+                        sizeof(args32.mode)))
+               return -EFAULT;
+
+       return 0;
+}
+
+struct vchiq_completion_data32 {
+       VCHIQ_REASON_T reason;
+       compat_uptr_t header;
+       compat_uptr_t service_userdata;
+       compat_uptr_t bulk_userdata;
+};
+
+struct vchiq_await_completion32 {
+       unsigned int count;
+       compat_uptr_t buf;
+       unsigned int msgbufsize;
+       unsigned int msgbufcount; /* IN/OUT */
+       compat_uptr_t msgbufs;
+};
+
+#define VCHIQ_IOC_AWAIT_COMPLETION32 \
+       _IOWR(VCHIQ_IOC_MAGIC, 7, struct vchiq_await_completion32)
+
+static long
+vchiq_compat_ioctl_await_completion(struct file *file,
+                                   unsigned int cmd,
+                                   unsigned long arg)
+{
+       VCHIQ_AWAIT_COMPLETION_T *args;
+       VCHIQ_COMPLETION_DATA_T *completion;
+       VCHIQ_COMPLETION_DATA_T completiontemp;
+       struct vchiq_await_completion32 args32;
+       struct vchiq_completion_data32 completion32;
+       unsigned int *msgbufcount32;
+       compat_uptr_t msgbuf32;
+       void *msgbuf;
+       void **msgbufptr;
+       long ret;
+
+       args = compat_alloc_user_space(sizeof(*args) +
+                                      sizeof(*completion) +
+                                      sizeof(*msgbufptr));
+       if (!args)
+               return -EFAULT;
+
+       completion = (VCHIQ_COMPLETION_DATA_T *)(args + 1);
+       msgbufptr = (void __user **)(completion + 1);
+
+       if (copy_from_user(&args32,
+                          (struct vchiq_completion_data32 *)arg,
+                          sizeof(args32)))
+               return -EFAULT;
+
+       if (put_user(args32.count, &args->count) ||
+           put_user(compat_ptr(args32.buf), &args->buf) ||
+           put_user(args32.msgbufsize, &args->msgbufsize) ||
+           put_user(args32.msgbufcount, &args->msgbufcount) ||
+           put_user(compat_ptr(args32.msgbufs), &args->msgbufs))
+               return -EFAULT;
+
+       /* These are simple cases, so just fall into the native handler */
+       if (!args32.count || !args32.buf || !args32.msgbufcount)
+               return vchiq_ioctl(file,
+                                  VCHIQ_IOC_AWAIT_COMPLETION,
+                                  (unsigned long)args);
+
+       /*
+        * These are the more complex cases.  Typical applications of this
+        * ioctl will use a very large count, with a very large msgbufcount.
+        * Since the native ioctl can asynchronously fill in the returned
+        * buffers and the application can in theory begin processing messages
+        * even before the ioctl returns, a bit of a trick is used here.
+        *
+        * By forcing both count and msgbufcount to be 1, it forces the native
+        * ioctl to only claim at most 1 message is available.   This tricks
+        * the calling application into thinking only 1 message was actually
+        * available in the queue so like all good applications it will retry
+        * waiting until all the required messages are received.
+        *
+        * This trick has been tested and proven to work with vchiq_test,
+        * Minecraft_PI, the "hello pi" examples, and various other
+        * applications that are included in Raspbian.
+        */
+
+       if (copy_from_user(&msgbuf32,
+                          compat_ptr(args32.msgbufs) +
+                          (sizeof(compat_uptr_t) *
+                          (args32.msgbufcount - 1)),
+                          sizeof(msgbuf32)))
+               return -EFAULT;
+
+       msgbuf = compat_ptr(msgbuf32);
+
+       if (copy_to_user(msgbufptr,
+                        &msgbuf,
+                        sizeof(msgbuf)))
+               return -EFAULT;
+
+       if (copy_to_user(&args->msgbufs,
+                        &msgbufptr,
+                        sizeof(msgbufptr)))
+               return -EFAULT;
+
+       if (put_user(1U, &args->count) ||
+           put_user(completion, &args->buf) ||
+           put_user(1U, &args->msgbufcount))
+               return -EFAULT;
+
+       ret = vchiq_ioctl(file,
+                         VCHIQ_IOC_AWAIT_COMPLETION,
+                         (unsigned long)args);
+
+       /*
+        * An return value of 0 here means that no messages where available
+        * in the message queue.  In this case the native ioctl does not
+        * return any data to the application at all.  Not even to update
+        * msgbufcount.  This functionality needs to be kept here for
+        * compatibility.
+        *
+        * Of course, < 0 means that an error occurred and no data is being
+        * returned.
+        *
+        * Since count and msgbufcount was forced to 1, that means
+        * the only other possible return value is 1. Meaning that 1 message
+        * was available, so that multiple message case does not need to be
+        * handled here.
+        */
+       if (ret <= 0)
+               return ret;
+
+       if (copy_from_user(&completiontemp, completion, sizeof(*completion)))
+               return -EFAULT;
+
+       completion32.reason = completiontemp.reason;
+       completion32.header = ptr_to_compat(completiontemp.header);
+       completion32.service_userdata =
+               ptr_to_compat(completiontemp.service_userdata);
+       completion32.bulk_userdata =
+               ptr_to_compat(completiontemp.bulk_userdata);
+
+       if (copy_to_user(compat_ptr(args32.buf),
+                        &completion32,
+                        sizeof(completion32)))
+               return -EFAULT;
+
+       args32.msgbufcount--;
+
+       msgbufcount32 =
+               &((struct vchiq_await_completion32 __user *)arg)->msgbufcount;
+
+       if (copy_to_user(msgbufcount32,
+                        &args32.msgbufcount,
+                        sizeof(args32.msgbufcount)))
+               return -EFAULT;
+
+       return 1;
+}
+
+struct vchiq_dequeue_message32 {
+       unsigned int handle;
+       int blocking;
+       unsigned int bufsize;
+       compat_uptr_t buf;
+};
+
+#define VCHIQ_IOC_DEQUEUE_MESSAGE32 \
+       _IOWR(VCHIQ_IOC_MAGIC, 8, struct vchiq_dequeue_message32)
+
+static long
+vchiq_compat_ioctl_dequeue_message(struct file *file,
+                                  unsigned int cmd,
+                                  unsigned long arg)
+{
+       VCHIQ_DEQUEUE_MESSAGE_T *args;
+       struct vchiq_dequeue_message32 args32;
+
+       args = compat_alloc_user_space(sizeof(*args));
+       if (!args)
+               return -EFAULT;
+
+       if (copy_from_user(&args32,
+                          (struct vchiq_dequeue_message32 *)arg,
+                          sizeof(args32)))
+               return -EFAULT;
+
+       if (put_user(args32.handle, &args->handle) ||
+           put_user(args32.blocking, &args->blocking) ||
+           put_user(args32.bufsize, &args->bufsize) ||
+           put_user(compat_ptr(args32.buf), &args->buf))
+               return -EFAULT;
+
+       return vchiq_ioctl(file, VCHIQ_IOC_DEQUEUE_MESSAGE,
+                          (unsigned long)args);
+}
+
+struct vchiq_get_config32 {
+       unsigned int config_size;
+       compat_uptr_t pconfig;
+};
+
+#define VCHIQ_IOC_GET_CONFIG32 \
+       _IOWR(VCHIQ_IOC_MAGIC, 10, struct vchiq_get_config32)
+
+static long
+vchiq_compat_ioctl_get_config(struct file *file,
+                             unsigned int cmd,
+                             unsigned long arg)
+{
+       VCHIQ_GET_CONFIG_T *args;
+       struct vchiq_get_config32 args32;
+
+       args = compat_alloc_user_space(sizeof(*args));
+       if (!args)
+               return -EFAULT;
+
+       if (copy_from_user(&args32,
+                          (struct vchiq_get_config32 *)arg,
+                          sizeof(args32)))
+               return -EFAULT;
+
+       if (put_user(args32.config_size, &args->config_size) ||
+           put_user(compat_ptr(args32.pconfig), &args->pconfig))
+               return -EFAULT;
+
+       return vchiq_ioctl(file, VCHIQ_IOC_GET_CONFIG, (unsigned long)args);
+}
+
+struct vchiq_dump_mem32 {
+       compat_uptr_t virt_addr;
+       u32 num_bytes;
+};
+
+#define VCHIQ_IOC_DUMP_PHYS_MEM32 \
+       _IOW(VCHIQ_IOC_MAGIC, 15, struct vchiq_dump_mem32)
+
+static long
+vchiq_compat_ioctl_dump_phys_mem(struct file *file,
+                                unsigned int cmd,
+                                unsigned long arg)
+{
+       VCHIQ_DUMP_MEM_T *args;
+       struct vchiq_dump_mem32 args32;
+
+       args = compat_alloc_user_space(sizeof(*args));
+       if (!args)
+               return -EFAULT;
+
+       if (copy_from_user(&args32,
+                          (struct vchiq_dump_mem32 *)arg,
+                          sizeof(args32)))
+               return -EFAULT;
+
+       if (put_user(compat_ptr(args32.virt_addr), &args->virt_addr) ||
+           put_user(args32.num_bytes, &args->num_bytes))
+               return -EFAULT;
+
+       return vchiq_ioctl(file, VCHIQ_IOC_DUMP_PHYS_MEM, (unsigned long)args);
+}
+
+static long
+vchiq_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       switch (cmd) {
+       case VCHIQ_IOC_CREATE_SERVICE32:
+               return vchiq_compat_ioctl_create_service(file, cmd, arg);
+       case VCHIQ_IOC_QUEUE_MESSAGE32:
+               return vchiq_compat_ioctl_queue_message(file, cmd, arg);
+       case VCHIQ_IOC_QUEUE_BULK_TRANSMIT32:
+       case VCHIQ_IOC_QUEUE_BULK_RECEIVE32:
+               return vchiq_compat_ioctl_queue_bulk(file, cmd, arg);
+       case VCHIQ_IOC_AWAIT_COMPLETION32:
+               return vchiq_compat_ioctl_await_completion(file, cmd, arg);
+       case VCHIQ_IOC_DEQUEUE_MESSAGE32:
+               return vchiq_compat_ioctl_dequeue_message(file, cmd, arg);
+       case VCHIQ_IOC_GET_CONFIG32:
+               return vchiq_compat_ioctl_get_config(file, cmd, arg);
+       case VCHIQ_IOC_DUMP_PHYS_MEM32:
+               return vchiq_compat_ioctl_dump_phys_mem(file, cmd, arg);
+       default:
+               return vchiq_ioctl(file, cmd, arg);
+       }
+}
+
+#endif
+
 /****************************************************************************
 *
 *   vchiq_open
 vchiq_fops = {
        .owner = THIS_MODULE,
        .unlocked_ioctl = vchiq_ioctl,
+#if defined(CONFIG_COMPAT)
+       .compat_ioctl = vchiq_compat_ioctl,
+#endif
        .open = vchiq_open,
        .release = vchiq_release,
        .read = vchiq_read