summaryrefslogtreecommitdiff
path: root/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
diff options
context:
space:
mode:
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.c1257
1 files changed, 598 insertions, 659 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 d4d811884861..01125d9f991b 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -53,7 +53,7 @@ int vchiq_susp_log_level = VCHIQ_LOG_ERROR;
struct user_service {
struct vchiq_service *service;
- void *userdata;
+ void __user *userdata;
struct vchiq_instance *instance;
char is_vchi;
char dequeue_pending;
@@ -75,7 +75,7 @@ struct bulk_waiter_node {
struct vchiq_instance {
struct vchiq_state *state;
- struct vchiq_completion_data completions[MAX_COMPLETIONS];
+ struct vchiq_completion_data_kernel completions[MAX_COMPLETIONS];
int completion_insert;
int completion_remove;
struct completion insert_event;
@@ -273,7 +273,7 @@ EXPORT_SYMBOL(vchiq_connect);
static enum vchiq_status vchiq_add_service(
struct vchiq_instance *instance,
- const struct vchiq_service_params *params,
+ const struct vchiq_service_params_kernel *params,
unsigned int *phandle)
{
enum vchiq_status status;
@@ -311,7 +311,7 @@ static enum vchiq_status vchiq_add_service(
enum vchiq_status vchiq_open_service(
struct vchiq_instance *instance,
- const struct vchiq_service_params *params,
+ const struct vchiq_service_params_kernel *params,
unsigned int *phandle)
{
enum vchiq_status status = VCHIQ_ERROR;
@@ -359,8 +359,9 @@ vchiq_bulk_transmit(unsigned int handle, const void *data,
switch (mode) {
case VCHIQ_BULK_MODE_NOCALLBACK:
case VCHIQ_BULK_MODE_CALLBACK:
- status = vchiq_bulk_transfer(handle, (void *)data, size,
- userdata, mode,
+ status = vchiq_bulk_transfer(handle,
+ (void *)data, NULL,
+ size, userdata, mode,
VCHIQ_BULK_TRANSMIT);
break;
case VCHIQ_BULK_MODE_BLOCKING:
@@ -396,7 +397,8 @@ enum vchiq_status vchiq_bulk_receive(unsigned int handle, void *data,
switch (mode) {
case VCHIQ_BULK_MODE_NOCALLBACK:
case VCHIQ_BULK_MODE_CALLBACK:
- status = vchiq_bulk_transfer(handle, data, size, userdata,
+ status = vchiq_bulk_transfer(handle, data, NULL,
+ size, userdata,
mode, VCHIQ_BULK_RECEIVE);
break;
case VCHIQ_BULK_MODE_BLOCKING:
@@ -430,6 +432,7 @@ vchiq_blocking_bulk_transfer(unsigned int handle, void *data,
struct vchiq_service *service;
enum vchiq_status status;
struct bulk_waiter_node *waiter = NULL;
+ bool found = false;
service = find_service_by_handle(handle);
if (!service)
@@ -443,17 +446,19 @@ vchiq_blocking_bulk_transfer(unsigned int handle, void *data,
list_for_each_entry(waiter, &instance->bulk_waiter_list, list) {
if (waiter->pid == current->pid) {
list_del(&waiter->list);
+ found = true;
break;
}
}
mutex_unlock(&instance->bulk_waiter_list_mutex);
- if (waiter) {
+ if (found) {
struct vchiq_bulk *bulk = waiter->bulk_waiter.bulk;
if (bulk) {
/* This thread has an outstanding bulk transfer. */
- if ((bulk->data != data) ||
+ /* FIXME: why compare a dma address to a pointer? */
+ if ((bulk->data != (dma_addr_t)(uintptr_t)data) ||
(bulk->size != size)) {
/* This is not a retry of the previous one.
* Cancel the signal when the transfer
@@ -464,9 +469,7 @@ vchiq_blocking_bulk_transfer(unsigned int handle, void *data,
spin_unlock(&bulk_waiter_spinlock);
}
}
- }
-
- if (!waiter) {
+ } else {
waiter = kzalloc(sizeof(struct bulk_waiter_node), GFP_KERNEL);
if (!waiter) {
vchiq_log_error(vchiq_core_log_level,
@@ -475,7 +478,8 @@ vchiq_blocking_bulk_transfer(unsigned int handle, void *data,
}
}
- status = vchiq_bulk_transfer(handle, data, size, &waiter->bulk_waiter,
+ status = vchiq_bulk_transfer(handle, data, NULL, size,
+ &waiter->bulk_waiter,
VCHIQ_BULK_MODE_BLOCKING, dir);
if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
!waiter->bulk_waiter.bulk) {
@@ -513,7 +517,7 @@ add_completion(struct vchiq_instance *instance, enum vchiq_reason reason,
struct vchiq_header *header, struct user_service *user_service,
void *bulk_userdata)
{
- struct vchiq_completion_data *completion;
+ struct vchiq_completion_data_kernel *completion;
int insert;
DEBUG_INITIALISE(g_state.local)
@@ -765,12 +769,13 @@ static ssize_t vchiq_ioc_copy_element_data(void *context, void *dest,
* vchiq_ioc_queue_message
*
**************************************************************************/
-static enum vchiq_status
+static int
vchiq_ioc_queue_message(unsigned int handle,
struct vchiq_element *elements,
unsigned long count)
{
struct vchiq_io_copy_callback_context context;
+ enum vchiq_status status = VCHIQ_SUCCESS;
unsigned long i;
size_t total_size = 0;
@@ -785,8 +790,459 @@ vchiq_ioc_queue_message(unsigned int handle,
total_size += elements[i].size;
}
- return vchiq_queue_message(handle, vchiq_ioc_copy_element_data,
- &context, total_size);
+ status = vchiq_queue_message(handle, vchiq_ioc_copy_element_data,
+ &context, total_size);
+
+ if (status == VCHIQ_ERROR)
+ return -EIO;
+ else if (status == VCHIQ_RETRY)
+ return -EINTR;
+ return 0;
+}
+
+static int vchiq_ioc_create_service(struct vchiq_instance *instance,
+ struct vchiq_create_service *args)
+{
+ struct user_service *user_service = NULL;
+ struct vchiq_service *service;
+ enum vchiq_status status = VCHIQ_SUCCESS;
+ struct vchiq_service_params_kernel params;
+ int srvstate;
+
+ user_service = kmalloc(sizeof(*user_service), GFP_KERNEL);
+ if (!user_service)
+ return -ENOMEM;
+
+ if (args->is_open) {
+ if (!instance->connected) {
+ kfree(user_service);
+ return -ENOTCONN;
+ }
+ srvstate = VCHIQ_SRVSTATE_OPENING;
+ } else {
+ srvstate = instance->connected ?
+ VCHIQ_SRVSTATE_LISTENING : VCHIQ_SRVSTATE_HIDDEN;
+ }
+
+ params = (struct vchiq_service_params_kernel) {
+ .fourcc = args->params.fourcc,
+ .callback = service_callback,
+ .userdata = user_service,
+ .version = args->params.version,
+ .version_min = args->params.version_min,
+ };
+ service = vchiq_add_service_internal(instance->state, &params,
+ srvstate, instance,
+ user_service_free);
+ if (!service) {
+ kfree(user_service);
+ return -EEXIST;
+ }
+
+ user_service->service = service;
+ user_service->userdata = args->params.userdata;
+ user_service->instance = instance;
+ user_service->is_vchi = (args->is_vchi != 0);
+ user_service->dequeue_pending = 0;
+ user_service->close_pending = 0;
+ user_service->message_available_pos = instance->completion_remove - 1;
+ user_service->msg_insert = 0;
+ user_service->msg_remove = 0;
+ init_completion(&user_service->insert_event);
+ init_completion(&user_service->remove_event);
+ init_completion(&user_service->close_event);
+
+ if (args->is_open) {
+ status = vchiq_open_service_internal(service, instance->pid);
+ if (status != VCHIQ_SUCCESS) {
+ vchiq_remove_service(service->handle);
+ return (status == VCHIQ_RETRY) ?
+ -EINTR : -EIO;
+ }
+ }
+ args->handle = service->handle;
+
+ return 0;
+}
+
+static int vchiq_ioc_dequeue_message(struct vchiq_instance *instance,
+ struct vchiq_dequeue_message *args)
+{
+ struct user_service *user_service;
+ struct vchiq_service *service;
+ struct vchiq_header *header;
+ int ret;
+
+ DEBUG_INITIALISE(g_state.local)
+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
+ service = find_service_for_instance(instance, args->handle);
+ if (!service)
+ return -EINVAL;
+
+ user_service = (struct user_service *)service->base.userdata;
+ if (user_service->is_vchi == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spin_lock(&msg_queue_spinlock);
+ if (user_service->msg_remove == user_service->msg_insert) {
+ if (!args->blocking) {
+ spin_unlock(&msg_queue_spinlock);
+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
+ ret = -EWOULDBLOCK;
+ goto out;
+ }
+ user_service->dequeue_pending = 1;
+ ret = 0;
+ do {
+ spin_unlock(&msg_queue_spinlock);
+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
+ if (wait_for_completion_interruptible(
+ &user_service->insert_event)) {
+ vchiq_log_info(vchiq_arm_log_level,
+ "DEQUEUE_MESSAGE interrupted");
+ ret = -EINTR;
+ break;
+ }
+ spin_lock(&msg_queue_spinlock);
+ } while (user_service->msg_remove ==
+ user_service->msg_insert);
+
+ if (ret)
+ goto out;
+ }
+
+ BUG_ON((int)(user_service->msg_insert -
+ user_service->msg_remove) < 0);
+
+ header = user_service->msg_queue[user_service->msg_remove &
+ (MSG_QUEUE_SIZE - 1)];
+ user_service->msg_remove++;
+ spin_unlock(&msg_queue_spinlock);
+
+ complete(&user_service->remove_event);
+ if (!header) {
+ ret = -ENOTCONN;
+ } else if (header->size <= args->bufsize) {
+ /* Copy to user space if msgbuf is not NULL */
+ if (!args->buf || (copy_to_user(args->buf,
+ header->data, header->size) == 0)) {
+ ret = header->size;
+ vchiq_release_message(service->handle, header);
+ } else
+ ret = -EFAULT;
+ } else {
+ vchiq_log_error(vchiq_arm_log_level,
+ "header %pK: bufsize %x < size %x",
+ header, args->bufsize, header->size);
+ WARN(1, "invalid size\n");
+ ret = -EMSGSIZE;
+ }
+ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
+out:
+ unlock_service(service);
+ return ret;
+}
+
+static int vchiq_irq_queue_bulk_tx_rx(struct vchiq_instance *instance,
+ struct vchiq_queue_bulk_transfer *args,
+ enum vchiq_bulk_dir dir,
+ enum vchiq_bulk_mode __user *mode)
+{
+ struct vchiq_service *service;
+ struct bulk_waiter_node *waiter = NULL;
+ bool found = false;
+ void *userdata = NULL;
+ int status = 0;
+ int ret;
+
+ service = find_service_for_instance(instance, args->handle);
+ if (!service)
+ return -EINVAL;
+
+ if (args->mode == VCHIQ_BULK_MODE_BLOCKING) {
+ waiter = kzalloc(sizeof(struct bulk_waiter_node),
+ GFP_KERNEL);
+ if (!waiter) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ userdata = &waiter->bulk_waiter;
+ } else if (args->mode == VCHIQ_BULK_MODE_WAITING) {
+ mutex_lock(&instance->bulk_waiter_list_mutex);
+ list_for_each_entry(waiter, &instance->bulk_waiter_list,
+ list) {
+ if (waiter->pid == current->pid) {
+ list_del(&waiter->list);
+ found = true;
+ break;
+ }
+ }
+ mutex_unlock(&instance->bulk_waiter_list_mutex);
+ if (!found) {
+ vchiq_log_error(vchiq_arm_log_level,
+ "no bulk_waiter found for pid %d",
+ current->pid);
+ ret = -ESRCH;
+ goto out;
+ }
+ vchiq_log_info(vchiq_arm_log_level,
+ "found bulk_waiter %pK for pid %d", waiter,
+ current->pid);
+ userdata = &waiter->bulk_waiter;
+ }
+
+ /*
+ * FIXME address space mismatch:
+ * args->data may be interpreted as a kernel pointer
+ * in create_pagelist() called from vchiq_bulk_transfer(),
+ * accessing kernel data instead of user space, based on the
+ * address.
+ */
+ status = vchiq_bulk_transfer(args->handle, NULL, args->data, args->size,
+ userdata, args->mode, dir);
+
+ if (!waiter) {
+ ret = 0;
+ goto out;
+ }
+
+ if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
+ !waiter->bulk_waiter.bulk) {
+ if (waiter->bulk_waiter.bulk) {
+ /* Cancel the signal when the transfer
+ ** completes. */
+ spin_lock(&bulk_waiter_spinlock);
+ waiter->bulk_waiter.bulk->userdata = NULL;
+ spin_unlock(&bulk_waiter_spinlock);
+ }
+ kfree(waiter);
+ ret = 0;
+ } else {
+ const enum vchiq_bulk_mode mode_waiting =
+ VCHIQ_BULK_MODE_WAITING;
+ waiter->pid = current->pid;
+ mutex_lock(&instance->bulk_waiter_list_mutex);
+ list_add(&waiter->list, &instance->bulk_waiter_list);
+ mutex_unlock(&instance->bulk_waiter_list_mutex);
+ vchiq_log_info(vchiq_arm_log_level,
+ "saved bulk_waiter %pK for pid %d",
+ waiter, current->pid);
+
+ ret = put_user(mode_waiting, mode);
+ }
+out:
+ unlock_service(service);
+ if (ret)
+ return ret;
+ else if (status == VCHIQ_ERROR)
+ return -EIO;
+ else if (status == VCHIQ_RETRY)
+ return -EINTR;
+ return 0;
+}
+
+/* read a user pointer value from an array pointers in user space */
+static inline int vchiq_get_user_ptr(void __user **buf, void __user *ubuf, int index)
+{
+ int ret;
+
+ if (in_compat_syscall()) {
+ compat_uptr_t ptr32;
+ compat_uptr_t __user *uptr = ubuf;
+ ret = get_user(ptr32, uptr + index);
+ *buf = compat_ptr(ptr32);
+ } else {
+ uintptr_t ptr, __user *uptr = ubuf;
+ ret = get_user(ptr, uptr + index);
+ *buf = (void __user *)ptr;
+ }
+
+ return ret;
+}
+
+struct vchiq_completion_data32 {
+ enum vchiq_reason reason;
+ compat_uptr_t header;
+ compat_uptr_t service_userdata;
+ compat_uptr_t bulk_userdata;
+};
+
+static int vchiq_put_completion(struct vchiq_completion_data __user *buf,
+ struct vchiq_completion_data *completion,
+ int index)
+{
+ struct vchiq_completion_data32 __user *buf32 = (void __user *)buf;
+
+ if (in_compat_syscall()) {
+ struct vchiq_completion_data32 tmp = {
+ .reason = completion->reason,
+ .header = ptr_to_compat(completion->header),
+ .service_userdata = ptr_to_compat(completion->service_userdata),
+ .bulk_userdata = ptr_to_compat(completion->bulk_userdata),
+ };
+ if (copy_to_user(&buf32[index], &tmp, sizeof(tmp)))
+ return -EFAULT;
+ } else {
+ if (copy_to_user(&buf[index], completion, sizeof(*completion)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int vchiq_ioc_await_completion(struct vchiq_instance *instance,
+ struct vchiq_await_completion *args,
+ int __user *msgbufcountp)
+{
+ int msgbufcount;
+ int remove;
+ int ret;
+
+ DEBUG_INITIALISE(g_state.local)
+
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+ if (!instance->connected) {
+ return -ENOTCONN;
+ }
+
+ mutex_lock(&instance->completion_mutex);
+
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+ while ((instance->completion_remove ==
+ instance->completion_insert)
+ && !instance->closing) {
+ int rc;
+
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+ mutex_unlock(&instance->completion_mutex);
+ rc = wait_for_completion_interruptible(
+ &instance->insert_event);
+ mutex_lock(&instance->completion_mutex);
+ if (rc) {
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+ vchiq_log_info(vchiq_arm_log_level,
+ "AWAIT_COMPLETION interrupted");
+ ret = -EINTR;
+ goto out;
+ }
+ }
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+
+ msgbufcount = args->msgbufcount;
+ remove = instance->completion_remove;
+
+ for (ret = 0; ret < args->count; ret++) {
+ struct vchiq_completion_data_kernel *completion;
+ struct vchiq_completion_data user_completion;
+ struct vchiq_service *service;
+ struct user_service *user_service;
+ struct vchiq_header *header;
+
+ if (remove == instance->completion_insert)
+ break;
+
+ completion = &instance->completions[
+ remove & (MAX_COMPLETIONS - 1)];
+
+ /*
+ * A read memory barrier is needed to stop
+ * prefetch of a stale completion record
+ */
+ rmb();
+
+ service = completion->service_userdata;
+ user_service = service->base.userdata;
+
+ memset(&user_completion, 0, sizeof(user_completion));
+ user_completion = (struct vchiq_completion_data) {
+ .reason = completion->reason,
+ .service_userdata = user_service->userdata,
+ };
+
+ header = completion->header;
+ if (header) {
+ void __user *msgbuf;
+ int msglen;
+
+ msglen = header->size + sizeof(struct vchiq_header);
+ /* This must be a VCHIQ-style service */
+ if (args->msgbufsize < msglen) {
+ vchiq_log_error(vchiq_arm_log_level,
+ "header %pK: msgbufsize %x < msglen %x",
+ header, args->msgbufsize, msglen);
+ WARN(1, "invalid message size\n");
+ if (ret == 0)
+ ret = -EMSGSIZE;
+ break;
+ }
+ if (msgbufcount <= 0)
+ /* Stall here for lack of a
+ ** buffer for the message. */
+ break;
+ /* Get the pointer from user space */
+ msgbufcount--;
+ if (vchiq_get_user_ptr(&msgbuf, args->msgbufs,
+ msgbufcount)) {
+ if (ret == 0)
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Copy the message to user space */
+ if (copy_to_user(msgbuf, header, msglen)) {
+ if (ret == 0)
+ ret = -EFAULT;
+ break;
+ }
+
+ /* Now it has been copied, the message
+ ** can be released. */
+ vchiq_release_message(service->handle, header);
+
+ /* The completion must point to the
+ ** msgbuf. */
+ user_completion.header = msgbuf;
+ }
+
+ if ((completion->reason == VCHIQ_SERVICE_CLOSED) &&
+ !instance->use_close_delivered)
+ unlock_service(service);
+
+ /*
+ * FIXME: address space mismatch, does bulk_userdata
+ * actually point to user or kernel memory?
+ */
+ user_completion.bulk_userdata = completion->bulk_userdata;
+
+ if (vchiq_put_completion(args->buf, &user_completion, ret)) {
+ if (ret == 0)
+ ret = -EFAULT;
+ break;
+ }
+
+ /*
+ * Ensure that the above copy has completed
+ * before advancing the remove pointer.
+ */
+ mb();
+ remove++;
+ instance->completion_remove = remove;
+ }
+
+ if (msgbufcount != args->msgbufcount) {
+ if (put_user(msgbufcount, msgbufcountp))
+ ret = -EFAULT;
+ }
+out:
+ if (ret)
+ complete(&instance->remove_event);
+ mutex_unlock(&instance->completion_mutex);
+ DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+
+ return ret;
}
/****************************************************************************
@@ -803,8 +1259,6 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
long ret = 0;
int i, rc;
- DEBUG_INITIALISE(g_state.local)
-
vchiq_log_trace(vchiq_arm_log_level,
"%s - instance %pK, cmd %s, arg %lx",
__func__, instance,
@@ -861,85 +1315,22 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
case VCHIQ_IOC_CREATE_SERVICE: {
+ struct vchiq_create_service __user *argp;
struct vchiq_create_service args;
- struct user_service *user_service = NULL;
- void *userdata;
- int srvstate;
- if (copy_from_user(&args, (const void __user *)arg,
- sizeof(args))) {
+ argp = (void __user *)arg;
+ if (copy_from_user(&args, argp, sizeof(args))) {
ret = -EFAULT;
break;
}
- user_service = kmalloc(sizeof(*user_service), GFP_KERNEL);
- if (!user_service) {
- ret = -ENOMEM;
+ ret = vchiq_ioc_create_service(instance, &args);
+ if (ret < 0)
break;
- }
-
- if (args.is_open) {
- if (!instance->connected) {
- ret = -ENOTCONN;
- kfree(user_service);
- break;
- }
- srvstate = VCHIQ_SRVSTATE_OPENING;
- } else {
- srvstate =
- instance->connected ?
- VCHIQ_SRVSTATE_LISTENING :
- VCHIQ_SRVSTATE_HIDDEN;
- }
-
- userdata = args.params.userdata;
- args.params.callback = service_callback;
- args.params.userdata = user_service;
- service = vchiq_add_service_internal(
- instance->state,
- &args.params, srvstate,
- instance, user_service_free);
-
- if (service) {
- user_service->service = service;
- user_service->userdata = userdata;
- user_service->instance = instance;
- user_service->is_vchi = (args.is_vchi != 0);
- user_service->dequeue_pending = 0;
- user_service->close_pending = 0;
- user_service->message_available_pos =
- instance->completion_remove - 1;
- user_service->msg_insert = 0;
- user_service->msg_remove = 0;
- init_completion(&user_service->insert_event);
- init_completion(&user_service->remove_event);
- init_completion(&user_service->close_event);
-
- if (args.is_open) {
- status = vchiq_open_service_internal
- (service, instance->pid);
- if (status != VCHIQ_SUCCESS) {
- vchiq_remove_service(service->handle);
- service = NULL;
- ret = (status == VCHIQ_RETRY) ?
- -EINTR : -EIO;
- break;
- }
- }
-
- if (copy_to_user((void __user *)
- &(((struct vchiq_create_service __user *)
- arg)->handle),
- (const void *)&service->handle,
- sizeof(service->handle))) {
- ret = -EFAULT;
- vchiq_remove_service(service->handle);
- }
- service = NULL;
- } else {
- ret = -EEXIST;
- kfree(user_service);
+ if (put_user(args.handle, &argp->handle)) {
+ vchiq_remove_service(args.handle);
+ ret = -EFAULT;
}
} break;
@@ -1020,9 +1411,8 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
if (copy_from_user(elements, args.elements,
args.count * sizeof(struct vchiq_element)) == 0)
- status = vchiq_ioc_queue_message
- (args.handle,
- elements, args.count);
+ ret = vchiq_ioc_queue_message(args.handle, elements,
+ args.count);
else
ret = -EFAULT;
} else {
@@ -1033,333 +1423,46 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case VCHIQ_IOC_QUEUE_BULK_TRANSMIT:
case VCHIQ_IOC_QUEUE_BULK_RECEIVE: {
struct vchiq_queue_bulk_transfer args;
- struct bulk_waiter_node *waiter = NULL;
+ struct vchiq_queue_bulk_transfer __user *argp;
enum vchiq_bulk_dir dir =
(cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
- if (copy_from_user(&args, (const void __user *)arg,
- sizeof(args))) {
+ argp = (void __user *)arg;
+ if (copy_from_user(&args, argp, sizeof(args))) {
ret = -EFAULT;
break;
}
- service = find_service_for_instance(instance, args.handle);
- if (!service) {
- ret = -EINVAL;
- break;
- }
-
- if (args.mode == VCHIQ_BULK_MODE_BLOCKING) {
- waiter = kzalloc(sizeof(struct bulk_waiter_node),
- GFP_KERNEL);
- if (!waiter) {
- ret = -ENOMEM;
- break;
- }
-
- args.userdata = &waiter->bulk_waiter;
- } else if (args.mode == VCHIQ_BULK_MODE_WAITING) {
- mutex_lock(&instance->bulk_waiter_list_mutex);
- list_for_each_entry(waiter, &instance->bulk_waiter_list,
- list) {
- if (waiter->pid == current->pid) {
- list_del(&waiter->list);
- break;
- }
- }
- mutex_unlock(&instance->bulk_waiter_list_mutex);
- if (!waiter) {
- vchiq_log_error(vchiq_arm_log_level,
- "no bulk_waiter found for pid %d",
- current->pid);
- ret = -ESRCH;
- break;
- }
- vchiq_log_info(vchiq_arm_log_level,
- "found bulk_waiter %pK for pid %d", waiter,
- current->pid);
- args.userdata = &waiter->bulk_waiter;
- }
-
- status = vchiq_bulk_transfer(args.handle, args.data, args.size,
- args.userdata, args.mode, dir);
-
- if (!waiter)
- break;
-
- if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) ||
- !waiter->bulk_waiter.bulk) {
- if (waiter->bulk_waiter.bulk) {
- /* Cancel the signal when the transfer
- ** completes. */
- spin_lock(&bulk_waiter_spinlock);
- waiter->bulk_waiter.bulk->userdata = NULL;
- spin_unlock(&bulk_waiter_spinlock);
- }
- kfree(waiter);
- } else {
- const enum vchiq_bulk_mode mode_waiting =
- VCHIQ_BULK_MODE_WAITING;
- waiter->pid = current->pid;
- mutex_lock(&instance->bulk_waiter_list_mutex);
- list_add(&waiter->list, &instance->bulk_waiter_list);
- mutex_unlock(&instance->bulk_waiter_list_mutex);
- vchiq_log_info(vchiq_arm_log_level,
- "saved bulk_waiter %pK for pid %d",
- waiter, current->pid);
-
- if (copy_to_user((void __user *)
- &(((struct vchiq_queue_bulk_transfer __user *)
- arg)->mode),
- (const void *)&mode_waiting,
- sizeof(mode_waiting)))
- ret = -EFAULT;
- }
+ ret = vchiq_irq_queue_bulk_tx_rx(instance, &args,
+ dir, &argp->mode);
} break;
case VCHIQ_IOC_AWAIT_COMPLETION: {
struct vchiq_await_completion args;
+ struct vchiq_await_completion __user *argp;
- DEBUG_TRACE(AWAIT_COMPLETION_LINE);
- if (!instance->connected) {
- ret = -ENOTCONN;
- break;
- }
-
- if (copy_from_user(&args, (const void __user *)arg,
- sizeof(args))) {
+ argp = (void __user *)arg;
+ if (copy_from_user(&args, argp, sizeof(args))) {
ret = -EFAULT;
break;
}
- mutex_lock(&instance->completion_mutex);
-
- DEBUG_TRACE(AWAIT_COMPLETION_LINE);
- while ((instance->completion_remove ==
- instance->completion_insert)
- && !instance->closing) {
- int rc;
-
- DEBUG_TRACE(AWAIT_COMPLETION_LINE);
- mutex_unlock(&instance->completion_mutex);
- rc = wait_for_completion_interruptible(
- &instance->insert_event);
- mutex_lock(&instance->completion_mutex);
- if (rc) {
- DEBUG_TRACE(AWAIT_COMPLETION_LINE);
- vchiq_log_info(vchiq_arm_log_level,
- "AWAIT_COMPLETION interrupted");
- ret = -EINTR;
- break;
- }
- }
- DEBUG_TRACE(AWAIT_COMPLETION_LINE);
-
- if (ret == 0) {
- int msgbufcount = args.msgbufcount;
- int remove = instance->completion_remove;
-
- for (ret = 0; ret < args.count; ret++) {
- struct vchiq_completion_data *completion;
- struct vchiq_service *service;
- struct user_service *user_service;
- struct vchiq_header *header;
-
- if (remove == instance->completion_insert)
- break;
-
- completion = &instance->completions[
- remove & (MAX_COMPLETIONS - 1)];
-
- /*
- * A read memory barrier is needed to stop
- * prefetch of a stale completion record
- */
- rmb();
-
- service = completion->service_userdata;
- user_service = service->base.userdata;
- completion->service_userdata =
- user_service->userdata;
-
- header = completion->header;
- if (header) {
- void __user *msgbuf;
- int msglen;
-
- msglen = header->size +
- sizeof(struct vchiq_header);
- /* This must be a VCHIQ-style service */
- if (args.msgbufsize < msglen) {
- vchiq_log_error(
- vchiq_arm_log_level,
- "header %pK: msgbufsize %x < msglen %x",
- header, args.msgbufsize,
- msglen);
- WARN(1, "invalid message "
- "size\n");
- if (ret == 0)
- ret = -EMSGSIZE;
- break;
- }
- if (msgbufcount <= 0)
- /* Stall here for lack of a
- ** buffer for the message. */
- break;
- /* Get the pointer from user space */
- msgbufcount--;
- if (copy_from_user(&msgbuf,
- (const void __user *)
- &args.msgbufs[msgbufcount],
- sizeof(msgbuf))) {
- if (ret == 0)
- ret = -EFAULT;
- break;
- }
-
- /* Copy the message to user space */
- if (copy_to_user(msgbuf, header,
- msglen)) {
- if (ret == 0)
- ret = -EFAULT;
- break;
- }
-
- /* Now it has been copied, the message
- ** can be released. */
- vchiq_release_message(service->handle,
- header);
-
- /* The completion must point to the
- ** msgbuf. */
- completion->header =
- (struct vchiq_header __force *)
- msgbuf;
- }
-
- if ((completion->reason ==
- VCHIQ_SERVICE_CLOSED) &&
- !instance->use_close_delivered)
- unlock_service(service);
-
- if (copy_to_user((void __user *)(
- (size_t)args.buf + ret *
- sizeof(struct vchiq_completion_data)),
- completion,
- sizeof(struct vchiq_completion_data))) {
- if (ret == 0)
- ret = -EFAULT;
- break;
- }
-
- /*
- * Ensure that the above copy has completed
- * before advancing the remove pointer.
- */
- mb();
- remove++;
- instance->completion_remove = remove;
- }
-
- if (msgbufcount != args.msgbufcount) {
- if (copy_to_user((void __user *)
- &((struct vchiq_await_completion *)arg)
- ->msgbufcount,
- &msgbufcount,
- sizeof(msgbufcount))) {
- ret = -EFAULT;
- }
- }
- }
-
- if (ret)
- complete(&instance->remove_event);
- mutex_unlock(&instance->completion_mutex);
- DEBUG_TRACE(AWAIT_COMPLETION_LINE);
+ ret = vchiq_ioc_await_completion(instance, &args,
+ &argp->msgbufcount);
} break;
case VCHIQ_IOC_DEQUEUE_MESSAGE: {
struct vchiq_dequeue_message args;
- struct user_service *user_service;
- struct vchiq_header *header;
- DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
if (copy_from_user(&args, (const void __user *)arg,
sizeof(args))) {
ret = -EFAULT;
break;
}
- service = find_service_for_instance(instance, args.handle);
- if (!service) {
- ret = -EINVAL;
- break;
- }
- user_service = (struct user_service *)service->base.userdata;
- if (user_service->is_vchi == 0) {
- ret = -EINVAL;
- break;
- }
- spin_lock(&msg_queue_spinlock);
- if (user_service->msg_remove == user_service->msg_insert) {
- if (!args.blocking) {
- spin_unlock(&msg_queue_spinlock);
- DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
- ret = -EWOULDBLOCK;
- break;
- }
- user_service->dequeue_pending = 1;
- do {
- spin_unlock(&msg_queue_spinlock);
- DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
- if (wait_for_completion_interruptible(
- &user_service->insert_event)) {
- vchiq_log_info(vchiq_arm_log_level,
- "DEQUEUE_MESSAGE interrupted");
- ret = -EINTR;
- break;
- }
- spin_lock(&msg_queue_spinlock);
- } while (user_service->msg_remove ==
- user_service->msg_insert);
-
- if (ret)
- break;
- }
-
- BUG_ON((int)(user_service->msg_insert -
- user_service->msg_remove) < 0);
-
- header = user_service->msg_queue[user_service->msg_remove &
- (MSG_QUEUE_SIZE - 1)];
- user_service->msg_remove++;
- spin_unlock(&msg_queue_spinlock);
-
- complete(&user_service->remove_event);
- if (!header)
- ret = -ENOTCONN;
- else if (header->size <= args.bufsize) {
- /* Copy to user space if msgbuf is not NULL */
- if (!args.buf ||
- (copy_to_user((void __user *)args.buf,
- header->data,
- header->size) == 0)) {
- ret = header->size;
- vchiq_release_message(
- service->handle,
- header);
- } else
- ret = -EFAULT;
- } else {
- vchiq_log_error(vchiq_arm_log_level,
- "header %pK: bufsize %x < size %x",
- header, args.bufsize, header->size);
- WARN(1, "invalid size\n");
- ret = -EMSGSIZE;
- }
- DEBUG_TRACE(DEQUEUE_MESSAGE_LINE);
+ ret = vchiq_ioc_dequeue_message(instance, &args);
} break;
case VCHIQ_IOC_GET_CLIENT_ID: {
@@ -1489,46 +1592,36 @@ static long
vchiq_compat_ioctl_create_service(
struct file *file,
unsigned int cmd,
- unsigned long arg)
+ struct vchiq_create_service32 __user *ptrargs32)
{
- struct vchiq_create_service __user *args;
- struct vchiq_create_service32 __user *ptrargs32 =
- (struct vchiq_create_service32 __user *)arg;
+ struct vchiq_create_service args;
struct vchiq_create_service32 args32;
long ret;
- args = compat_alloc_user_space(sizeof(*args));
- if (!args)
- return -EFAULT;
-
if (copy_from_user(&args32, ptrargs32, 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);
+ args = (struct vchiq_create_service) {
+ .params = {
+ .fourcc = args32.params.fourcc,
+ .callback = compat_ptr(args32.params.callback),
+ .userdata = compat_ptr(args32.params.userdata),
+ .version = args32.params.version,
+ .version_min = args32.params.version_min,
+ },
+ .is_open = args32.is_open,
+ .is_vchi = args32.is_vchi,
+ .handle = args32.handle,
+ };
+ ret = vchiq_ioc_create_service(file->private_data, &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)))
+ if (put_user(args.handle, &ptrargs32->handle)) {
+ vchiq_remove_service(args.handle);
return -EFAULT;
+ }
return 0;
}
@@ -1550,55 +1643,53 @@ struct vchiq_queue_message32 {
static long
vchiq_compat_ioctl_queue_message(struct file *file,
unsigned int cmd,
- unsigned long arg)
+ struct vchiq_queue_message32 __user *arg)
{
- struct vchiq_queue_message __user *args;
- struct vchiq_element __user *elements;
+ struct vchiq_queue_message args;
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));
+ struct vchiq_service *service;
+ int ret;
- if (!args)
+ if (copy_from_user(&args32, arg, sizeof(args32)))
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;
+ args = (struct vchiq_queue_message) {
+ .handle = args32.handle,
+ .count = args32.count,
+ .elements = compat_ptr(args32.elements),
+ };
if (args32.count > MAX_ELEMENTS)
return -EINVAL;
- if (args32.elements && args32.count) {
- struct vchiq_element32 tempelement32[MAX_ELEMENTS];
+ service = find_service_for_instance(file->private_data, args.handle);
+ if (!service)
+ return -EINVAL;
- elements = (struct vchiq_element __user *)(args + 1);
+ if (args32.elements && args32.count) {
+ struct vchiq_element32 element32[MAX_ELEMENTS];
+ struct vchiq_element elements[MAX_ELEMENTS];
+ unsigned int count;
- if (copy_from_user(&tempelement32,
- compat_ptr(args32.elements),
- sizeof(tempelement32)))
+ if (copy_from_user(&element32, args.elements,
+ sizeof(element32))) {
+ unlock_service(service);
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;
+ elements[count].data =
+ compat_ptr(element32[count].data);
+ elements[count].size = element32[count].size;
}
-
- if (put_user(elements, &args->elements))
- return -EFAULT;
+ ret = vchiq_ioc_queue_message(args.handle, elements,
+ args.count);
+ } else {
+ ret = -EINVAL;
}
+ unlock_service(service);
- return vchiq_ioctl(file, VCHIQ_IOC_QUEUE_MESSAGE, (unsigned long)args);
+ return ret;
}
struct vchiq_queue_bulk_transfer32 {
@@ -1617,56 +1708,28 @@ struct vchiq_queue_bulk_transfer32 {
static long
vchiq_compat_ioctl_queue_bulk(struct file *file,
unsigned int cmd,
- unsigned long arg)
+ struct vchiq_queue_bulk_transfer32 __user *argp)
{
- struct vchiq_queue_bulk_transfer __user *args;
struct vchiq_queue_bulk_transfer32 args32;
- struct vchiq_queue_bulk_transfer32 __user *ptrargs32 =
- (struct vchiq_queue_bulk_transfer32 __user *)arg;
- long ret;
-
- args = compat_alloc_user_space(sizeof(*args));
- if (!args)
- return -EFAULT;
-
- if (copy_from_user(&args32, ptrargs32, 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;
+ struct vchiq_queue_bulk_transfer args;
+ enum vchiq_bulk_dir dir = (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ?
+ VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE;
- if (get_user(args32.mode, &args->mode))
+ if (copy_from_user(&args32, argp, sizeof(args32)))
return -EFAULT;
- if (copy_to_user(&ptrargs32->mode,
- &args32.mode,
- sizeof(args32.mode)))
- return -EFAULT;
+ args = (struct vchiq_queue_bulk_transfer) {
+ .handle = args32.handle,
+ .data = compat_ptr(args32.data),
+ .size = args32.size,
+ .userdata = compat_ptr(args32.userdata),
+ .mode = args32.mode,
+ };
- return 0;
+ return vchiq_irq_queue_bulk_tx_rx(file->private_data, &args,
+ dir, &argp->mode);
}
-struct vchiq_completion_data32 {
- enum vchiq_reason 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;
@@ -1681,141 +1744,24 @@ struct vchiq_await_completion32 {
static long
vchiq_compat_ioctl_await_completion(struct file *file,
unsigned int cmd,
- unsigned long arg)
+ struct vchiq_await_completion32 __user *argp)
{
- struct vchiq_await_completion __user *args;
- struct vchiq_completion_data __user *completion;
- struct vchiq_completion_data completiontemp;
+ struct vchiq_await_completion args;
struct vchiq_await_completion32 args32;
- struct vchiq_completion_data32 completion32;
- unsigned int __user *msgbufcount32;
- unsigned int msgbufcount_native;
- compat_uptr_t msgbuf32;
- void __user *msgbuf;
- void * __user *msgbufptr;
- long ret;
-
- args = compat_alloc_user_space(sizeof(*args) +
- sizeof(*completion) +
- sizeof(*msgbufptr));
- if (!args)
- return -EFAULT;
-
- completion = (struct vchiq_completion_data __user *)(args + 1);
- msgbufptr = (void * __user *)(completion + 1);
-
- if (copy_from_user(&args32,
- (struct vchiq_completion_data32 __user *)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)))
+ if (copy_from_user(&args32, argp, sizeof(args32)))
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;
-
- if (get_user(msgbufcount_native, &args->msgbufcount))
- return -EFAULT;
-
- if (!msgbufcount_native)
- args32.msgbufcount--;
-
- msgbufcount32 =
- &((struct vchiq_await_completion32 __user *)arg)->msgbufcount;
-
- if (copy_to_user(msgbufcount32,
- &args32.msgbufcount,
- sizeof(args32.msgbufcount)))
- return -EFAULT;
+ args = (struct vchiq_await_completion) {
+ .count = args32.count,
+ .buf = compat_ptr(args32.buf),
+ .msgbufsize = args32.msgbufsize,
+ .msgbufcount = args32.msgbufcount,
+ .msgbufs = compat_ptr(args32.msgbufs),
+ };
- return 1;
+ return vchiq_ioc_await_completion(file->private_data, &args,
+ &argp->msgbufcount);
}
struct vchiq_dequeue_message32 {
@@ -1831,28 +1777,22 @@ struct vchiq_dequeue_message32 {
static long
vchiq_compat_ioctl_dequeue_message(struct file *file,
unsigned int cmd,
- unsigned long arg)
+ struct vchiq_dequeue_message32 __user *arg)
{
- struct vchiq_dequeue_message __user *args;
struct vchiq_dequeue_message32 args32;
+ struct vchiq_dequeue_message args;
- args = compat_alloc_user_space(sizeof(*args));
- if (!args)
- return -EFAULT;
-
- if (copy_from_user(&args32,
- (struct vchiq_dequeue_message32 __user *)arg,
- sizeof(args32)))
+ if (copy_from_user(&args32, 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;
+ args = (struct vchiq_dequeue_message) {
+ .handle = args32.handle,
+ .blocking = args32.blocking,
+ .bufsize = args32.bufsize,
+ .buf = compat_ptr(args32.buf),
+ };
- return vchiq_ioctl(file, VCHIQ_IOC_DEQUEUE_MESSAGE,
- (unsigned long)args);
+ return vchiq_ioc_dequeue_message(file->private_data, &args);
}
struct vchiq_get_config32 {
@@ -1866,46 +1806,45 @@ struct vchiq_get_config32 {
static long
vchiq_compat_ioctl_get_config(struct file *file,
unsigned int cmd,
- unsigned long arg)
+ struct vchiq_get_config32 __user *arg)
{
- struct vchiq_get_config __user *args;
struct vchiq_get_config32 args32;
+ struct vchiq_config config;
+ void __user *ptr;
- args = compat_alloc_user_space(sizeof(*args));
- if (!args)
- return -EFAULT;
-
- if (copy_from_user(&args32,
- (struct vchiq_get_config32 __user *)arg,
- sizeof(args32)))
+ if (copy_from_user(&args32, arg, sizeof(args32)))
return -EFAULT;
+ if (args32.config_size > sizeof(config))
+ return -EINVAL;
- if (put_user(args32.config_size, &args->config_size) ||
- put_user(compat_ptr(args32.pconfig), &args->pconfig))
+ vchiq_get_config(&config);
+ ptr = compat_ptr(args32.pconfig);
+ if (copy_to_user(ptr, &config, args32.config_size))
return -EFAULT;
- return vchiq_ioctl(file, VCHIQ_IOC_GET_CONFIG, (unsigned long)args);
+ return 0;
}
static long
vchiq_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
+ void __user *argp = compat_ptr(arg);
switch (cmd) {
case VCHIQ_IOC_CREATE_SERVICE32:
- return vchiq_compat_ioctl_create_service(file, cmd, arg);
+ return vchiq_compat_ioctl_create_service(file, cmd, argp);
case VCHIQ_IOC_QUEUE_MESSAGE32:
- return vchiq_compat_ioctl_queue_message(file, cmd, arg);
+ return vchiq_compat_ioctl_queue_message(file, cmd, argp);
case VCHIQ_IOC_QUEUE_BULK_TRANSMIT32:
case VCHIQ_IOC_QUEUE_BULK_RECEIVE32:
- return vchiq_compat_ioctl_queue_bulk(file, cmd, arg);
+ return vchiq_compat_ioctl_queue_bulk(file, cmd, argp);
case VCHIQ_IOC_AWAIT_COMPLETION32:
- return vchiq_compat_ioctl_await_completion(file, cmd, arg);
+ return vchiq_compat_ioctl_await_completion(file, cmd, argp);
case VCHIQ_IOC_DEQUEUE_MESSAGE32:
- return vchiq_compat_ioctl_dequeue_message(file, cmd, arg);
+ return vchiq_compat_ioctl_dequeue_message(file, cmd, argp);
case VCHIQ_IOC_GET_CONFIG32:
- return vchiq_compat_ioctl_get_config(file, cmd, arg);
+ return vchiq_compat_ioctl_get_config(file, cmd, argp);
default:
- return vchiq_ioctl(file, cmd, arg);
+ return vchiq_ioctl(file, cmd, (unsigned long)argp);
}
}
@@ -2018,7 +1957,7 @@ static int vchiq_release(struct inode *inode, struct file *file)
/* Release any closed services */
while (instance->completion_remove !=
instance->completion_insert) {
- struct vchiq_completion_data *completion;
+ struct vchiq_completion_data_kernel *completion;
struct vchiq_service *service;
completion = &instance->completions[
@@ -2283,7 +2222,7 @@ vchiq_keepalive_thread_func(void *v)
struct vchiq_instance *instance;
unsigned int ka_handle;
- struct vchiq_service_params params = {
+ struct vchiq_service_params_kernel params = {
.fourcc = VCHIQ_MAKE_FOURCC('K', 'E', 'E', 'P'),
.callback = vchiq_keepalive_vchiq_callback,
.version = KEEPALIVE_VER,