aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-05-05 18:16:23 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2017-05-05 18:16:23 -0700
commitc6a677c6f37bb7abc85ba7e3465e82b9f7eb1d91 (patch)
tree9d0d4bb2e150837297cddc5be7f1b4950e9ab228 /drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
parentMerge tag 'media/v4.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media (diff)
parentstaging: fsl-mc/dpio: add cpu <--> LE conversion for dpaa2_fd (diff)
downloadlinux-dev-c6a677c6f37bb7abc85ba7e3465e82b9f7eb1d91.tar.xz
linux-dev-c6a677c6f37bb7abc85ba7e3465e82b9f7eb1d91.zip
Merge tag 'staging-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging/IIO updates from Greg KH: "Here is the big staging tree update for 4.12-rc1. It's a big one, adding about 350k new lines of crap^Wcode, mostly all in a big dump of media drivers from Intel. But there's other new drivers in here as well, yet-another-wifi driver, new IIO drivers, and a new crypto accelerator. We also deleted a bunch of stuff, mostly in patch cleanups, but also the Android ION code has shrunk a lot, and the Android low memory killer driver was finally deleted, much to the celebration of the -mm developers. All of these have been in linux-next with a few build issues that will show up when you merge to your tree" Merge conflicts in the new rtl8723bs driver (due to the wifi changes this merge window) handled as per linux-next, courtesy of Stephen Rothwell. * tag 'staging-4.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1182 commits) staging: fsl-mc/dpio: add cpu <--> LE conversion for dpaa2_fd staging: ks7010: remove line continuations in quoted strings staging: vt6656: use tabs instead of spaces staging: android: ion: Fix unnecessary initialization of static variable staging: media: atomisp: fix range checking on clk_num staging: media: atomisp: fix misspelled word in comment staging: media: atomisp: kmap() can't fail staging: atomisp: remove #ifdef for runtime PM functions staging: atomisp: satm include directory is gone atomisp: remove some more unused files atomisp: remove hmm_load/store/clear indirections atomisp: kill off mmgr_free atomisp: clean up the hmm init/cleanup indirections atomisp: handle allocation calls before init in the hmm layer staging: fsl-dpaa2/eth: Add maintainer for Ethernet driver staging: fsl-dpaa2/eth: Add TODO file staging: fsl-dpaa2/eth: Add trace points staging: fsl-dpaa2/eth: Add driver specific stats staging: fsl-dpaa2/eth: Add ethtool support staging: fsl-dpaa2/eth: Add Freescale DPAA2 Ethernet driver ...
Diffstat (limited to 'drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c')
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c550
1 files changed, 539 insertions, 11 deletions
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
index 8a0d214f6e9b..e823f1d5d177 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -48,6 +48,7 @@
#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"
@@ -194,8 +195,10 @@ static const char *const ioctl_names[] = {
vchiq_static_assert(ARRAY_SIZE(ioctl_names) ==
(VCHIQ_IOC_MAX + 1));
+#if defined(CONFIG_BCM2835_VCHIQ_SUPPORT_MEMDUMP)
static void
dump_phys_mem(void *virt_addr, u32 num_bytes);
+#endif
/****************************************************************************
*
@@ -210,6 +213,7 @@ add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason,
{
VCHIQ_COMPLETION_DATA_T *completion;
int insert;
+
DEBUG_INITIALISE(g_state.local)
insert = instance->completion_insert;
@@ -281,6 +285,7 @@ service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header,
VCHIQ_SERVICE_T *service;
VCHIQ_INSTANCE_T instance;
bool skip_completion = false;
+
DEBUG_INITIALISE(g_state.local)
DEBUG_TRACE(SERVICE_CALLBACK_LINE);
@@ -316,6 +321,7 @@ service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header,
if ((user_service->message_available_pos -
instance->completion_remove) < 0) {
VCHIQ_STATUS_T status;
+
vchiq_log_info(vchiq_arm_log_level,
"Inserting extra MESSAGE_AVAILABLE");
DEBUG_TRACE(SERVICE_CALLBACK_LINE);
@@ -407,7 +413,7 @@ static void close_delivered(USER_SERVICE_T *user_service)
}
struct vchiq_io_copy_callback_context {
- VCHIQ_ELEMENT_T *current_element;
+ struct vchiq_element *current_element;
size_t current_element_offset;
unsigned long elements_to_go;
size_t current_offset;
@@ -484,7 +490,7 @@ vchiq_ioc_copy_element_data(
**************************************************************************/
static VCHIQ_STATUS_T
vchiq_ioc_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
- VCHIQ_ELEMENT_T *elements,
+ struct vchiq_element *elements,
unsigned long count)
{
struct vchiq_io_copy_callback_context context;
@@ -520,6 +526,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
VCHIQ_SERVICE_T *service = NULL;
long ret = 0;
int i, rc;
+
DEBUG_INITIALISE(g_state.local)
vchiq_log_trace(vchiq_arm_log_level,
@@ -742,6 +749,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case VCHIQ_IOC_QUEUE_MESSAGE: {
VCHIQ_QUEUE_MESSAGE_T args;
+
if (copy_from_user
(&args, (const void __user *)arg,
sizeof(args)) != 0) {
@@ -753,9 +761,10 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if ((service != NULL) && (args.count <= MAX_ELEMENTS)) {
/* Copy elements into kernel space */
- VCHIQ_ELEMENT_T elements[MAX_ELEMENTS];
+ struct vchiq_element elements[MAX_ELEMENTS];
+
if (copy_from_user(elements, args.elements,
- args.count * sizeof(VCHIQ_ELEMENT_T)) == 0)
+ args.count * sizeof(struct vchiq_element)) == 0)
status = vchiq_ioc_queue_message
(args.handle,
elements, args.count);
@@ -770,6 +779,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case VCHIQ_IOC_QUEUE_BULK_RECEIVE: {
VCHIQ_QUEUE_BULK_TRANSFER_T args;
struct bulk_waiter_node *waiter = NULL;
+
VCHIQ_BULK_DIR_T dir =
(cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
@@ -797,6 +807,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
args.userdata = &waiter->bulk_waiter;
} else if (args.mode == VCHIQ_BULK_MODE_WAITING) {
struct list_head *pos;
+
mutex_lock(&instance->bulk_waiter_list_mutex);
list_for_each(pos, &instance->bulk_waiter_list) {
if (list_entry(pos, struct bulk_waiter_node,
@@ -882,6 +893,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
instance->completion_insert)
&& !instance->closing) {
int rc;
+
DEBUG_TRACE(AWAIT_COMPLETION_LINE);
mutex_unlock(&instance->completion_mutex);
rc = down_interruptible(&instance->insert_event);
@@ -1149,6 +1161,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
args.handle, args.option, args.value);
} break;
+#if defined(CONFIG_BCM2835_VCHIQ_SUPPORT_MEMDUMP)
case VCHIQ_IOC_DUMP_PHYS_MEM: {
VCHIQ_DUMP_MEM_T args;
@@ -1160,6 +1173,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
dump_phys_mem(args.virt_addr, args.num_bytes);
} break;
+#endif
case VCHIQ_IOC_LIB_VERSION: {
unsigned int lib_version = (unsigned int)arg;
@@ -1219,6 +1233,491 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
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;
+ struct vchiq_element *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 = (struct vchiq_element __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);
+}
+
+#if defined(CONFIG_BCM2835_VCHIQ_SUPPORT_MEMDUMP)
+
+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);
+}
+
+#endif
+
+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);
+#if defined(CONFIG_BCM2835_VCHIQ_SUPPORT_MEMDUMP)
+ case VCHIQ_IOC_DUMP_PHYS_MEM32:
+ return vchiq_compat_ioctl_dump_phys_mem(file, cmd, arg);
+#endif
+ default:
+ return vchiq_ioctl(file, cmd, arg);
+ }
+}
+
+#endif
+
/****************************************************************************
*
* vchiq_open
@@ -1229,6 +1728,7 @@ static int
vchiq_open(struct inode *inode, struct file *file)
{
int dev = iminor(inode) & 0x0f;
+
vchiq_log_info(vchiq_arm_log_level, "vchiq_open");
switch (dev) {
case VCHIQ_MINOR: {
@@ -1284,6 +1784,7 @@ vchiq_release(struct inode *inode, struct file *file)
{
int dev = iminor(inode) & 0x0f;
int ret = 0;
+
switch (dev) {
case VCHIQ_MINOR: {
VCHIQ_INSTANCE_T instance = file->private_data;
@@ -1365,6 +1866,7 @@ vchiq_release(struct inode *inode, struct file *file)
instance->completion_insert) {
VCHIQ_COMPLETION_DATA_T *completion;
VCHIQ_SERVICE_T *service;
+
completion = &instance->completions[
instance->completion_remove &
(MAX_COMPLETIONS - 1)];
@@ -1387,9 +1889,11 @@ vchiq_release(struct inode *inode, struct file *file)
{
struct list_head *pos, *next;
+
list_for_each_safe(pos, next,
&instance->bulk_waiter_list) {
struct bulk_waiter_node *waiter;
+
waiter = list_entry(pos,
struct bulk_waiter_node,
list);
@@ -1430,8 +1934,10 @@ vchiq_dump(void *dump_context, const char *str, int len)
if (context->actual < context->space) {
int copy_bytes;
+
if (context->offset > 0) {
int skip_bytes = min(len, (int)context->offset);
+
str += skip_bytes;
len -= skip_bytes;
context->offset -= skip_bytes;
@@ -1452,6 +1958,7 @@ vchiq_dump(void *dump_context, const char *str, int len)
** carriage return. */
if ((len == 0) && (str[copy_bytes - 1] == '\0')) {
char cr = '\n';
+
if (copy_to_user(context->buf + context->actual - 1,
&cr, 1))
context->actual = -EFAULT;
@@ -1547,6 +2054,8 @@ vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service)
*
***************************************************************************/
+#if defined(CONFIG_BCM2835_VCHIQ_SUPPORT_MEMDUMP)
+
static void
dump_phys_mem(void *virt_addr, u32 num_bytes)
{
@@ -1570,10 +2079,10 @@ dump_phys_mem(void *virt_addr, u32 num_bytes)
offset = (int)(long)virt_addr & (PAGE_SIZE - 1);
end_offset = (int)(long)end_virt_addr & (PAGE_SIZE - 1);
- num_pages = (offset + num_bytes + PAGE_SIZE - 1) / PAGE_SIZE;
+ num_pages = DIV_ROUND_UP(offset + num_bytes, PAGE_SIZE);
pages = kmalloc(sizeof(struct page *) * num_pages, GFP_KERNEL);
- if (pages == NULL) {
+ if (!pages) {
vchiq_log_error(vchiq_arm_log_level,
"Unable to allocation memory for %d pages\n",
num_pages);
@@ -1599,17 +2108,14 @@ dump_phys_mem(void *virt_addr, u32 num_bytes)
}
while (offset < end_offset) {
-
int page_offset = offset % PAGE_SIZE;
- page_idx = offset / PAGE_SIZE;
+ page_idx = offset / PAGE_SIZE;
if (page_idx != prev_idx) {
-
if (page != NULL)
kunmap(page);
page = pages[page_idx];
kmapped_virt_ptr = kmap(page);
-
prev_idx = page_idx;
}
@@ -1632,6 +2138,8 @@ out:
kfree(pages);
}
+#endif
+
/****************************************************************************
*
* vchiq_read
@@ -1643,6 +2151,7 @@ vchiq_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
DUMP_CONTEXT_T context;
+
context.buf = buf;
context.actual = 0;
context.space = count;
@@ -1673,6 +2182,9 @@ static const struct file_operations
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
@@ -1686,6 +2198,7 @@ int
vchiq_videocore_wanted(VCHIQ_STATE_T *state)
{
VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
+
if (!arm_state)
/* autosuspend not supported - always return wanted */
return 1;
@@ -1753,6 +2266,7 @@ vchiq_keepalive_thread_func(void *v)
while (1) {
long rc = 0, uc = 0;
+
if (wait_for_completion_interruptible(&arm_state->ka_evt)
!= 0) {
vchiq_log_error(vchiq_susp_log_level,
@@ -1982,6 +2496,7 @@ static inline int
need_resume(VCHIQ_STATE_T *state)
{
VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
+
return (arm_state->vc_suspend_state > VC_SUSPEND_IDLE) &&
(arm_state->vc_resume_state < VC_RESUME_REQUESTED) &&
vchiq_videocore_wanted(state);
@@ -2155,6 +2670,7 @@ output_timeout_error(VCHIQ_STATE_T *state)
}
for (i = 0; i < active_services; i++) {
VCHIQ_SERVICE_T *service_ptr = state->services[i];
+
if (service_ptr && service_ptr->service_use_count &&
(service_ptr->srvstate != VCHIQ_SRVSTATE_FREE)) {
snprintf(err, sizeof(err), " %c%c%c%c(%d) service has "
@@ -2502,6 +3018,7 @@ vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service,
if (ret == VCHIQ_SUCCESS) {
VCHIQ_STATUS_T status = VCHIQ_SUCCESS;
long ack_cnt = atomic_xchg(&arm_state->ka_use_ack_count, 0);
+
while (ack_cnt && (status == VCHIQ_SUCCESS)) {
/* Send the use notify to videocore */
status = vchiq_send_remote_use_active(state);
@@ -2584,6 +3101,7 @@ void
vchiq_on_remote_use(VCHIQ_STATE_T *state)
{
VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
+
vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
atomic_inc(&arm_state->ka_use_count);
complete(&arm_state->ka_evt);
@@ -2593,6 +3111,7 @@ void
vchiq_on_remote_release(VCHIQ_STATE_T *state)
{
VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
+
vchiq_log_trace(vchiq_susp_log_level, "%s", __func__);
atomic_inc(&arm_state->ka_release_count);
complete(&arm_state->ka_evt);
@@ -2621,6 +3140,7 @@ vchiq_instance_get_use_count(VCHIQ_INSTANCE_T instance)
{
VCHIQ_SERVICE_T *service;
int use_count = 0, i;
+
i = 0;
while ((service = next_service_by_instance(instance->state,
instance, &i)) != NULL) {
@@ -2647,6 +3167,7 @@ vchiq_instance_set_trace(VCHIQ_INSTANCE_T instance, int trace)
{
VCHIQ_SERVICE_T *service;
int i;
+
i = 0;
while ((service = next_service_by_instance(instance->state,
instance, &i)) != NULL) {
@@ -2660,6 +3181,7 @@ static void suspend_timer_callback(unsigned long context)
{
VCHIQ_STATE_T *state = (VCHIQ_STATE_T *)context;
VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
+
if (!arm_state)
goto out;
vchiq_log_info(vchiq_susp_log_level,
@@ -2674,6 +3196,7 @@ vchiq_use_service_no_resume(VCHIQ_SERVICE_HANDLE_T handle)
{
VCHIQ_STATUS_T ret = VCHIQ_ERROR;
VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
+
if (service) {
ret = vchiq_use_internal(service->state, service,
USE_TYPE_SERVICE_NO_RESUME);
@@ -2687,6 +3210,7 @@ vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle)
{
VCHIQ_STATUS_T ret = VCHIQ_ERROR;
VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
+
if (service) {
ret = vchiq_use_internal(service->state, service,
USE_TYPE_SERVICE);
@@ -2700,6 +3224,7 @@ vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle)
{
VCHIQ_STATUS_T ret = VCHIQ_ERROR;
VCHIQ_SERVICE_T *service = find_service_by_handle(handle);
+
if (service) {
ret = vchiq_release_internal(service->state, service);
unlock_service(service);
@@ -2744,6 +3269,7 @@ vchiq_dump_service_use_state(VCHIQ_STATE_T *state)
for (i = 0; (i < active_services) && (j < local_max_services); i++) {
VCHIQ_SERVICE_T *service_ptr = state->services[i];
+
if (!service_ptr)
continue;
@@ -2832,12 +3358,14 @@ void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state,
VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate)
{
VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state);
+
vchiq_log_info(vchiq_susp_log_level, "%d: %s->%s", state->id,
get_conn_state_name(oldstate), get_conn_state_name(newstate));
if (state->conn_state == VCHIQ_CONNSTATE_CONNECTED) {
write_lock_bh(&arm_state->susp_res_lock);
if (!arm_state->first_connect) {
char threadname[16];
+
arm_state->first_connect = 1;
write_unlock_bh(&arm_state->susp_res_lock);
snprintf(threadname, sizeof(threadname), "vchiq-keep/%d",
@@ -2962,6 +3490,6 @@ static struct platform_driver vchiq_driver = {
};
module_platform_driver(vchiq_driver);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Videocore VCHIQ driver");
MODULE_AUTHOR("Broadcom Corporation");