summaryrefslogtreecommitdiff
path: root/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
diff options
context:
space:
mode:
authorMichael Zoran <mzoran@crowfest.net>2016-10-30 05:55:07 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-11-07 11:03:14 +0100
commit49bec49fd7f273ec114e2e533c1bb8f21a654aaf (patch)
tree498abe008b45712e459d8c76d8eac15fa124a495 /drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
parentc01cc53d4eb81a626ca289d784143f22c2fe63ab (diff)
staging: vc04_services: remove vchiq_copy_from_user
The vchiq_copy_from_user function is not portable and is consider "bad practice." Replace this function with a callback based mechanism that is passed downward on the stack. When it is actually time to copy the data, the callback is called to copy the data into the message. This callback is provided internally for userland calls through ioctls on the device. NOTE: Internal clients will need to be modified to work with the new internal API. Test Run: vchiq_test -p 1 vchiq_test -f 10 Both tests pass. Internal API Changes: Change vchi_msg_queue to: int32_t vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle, ssize_t (*copy_callback)(void *context, void *dest, size_t offset, size_t maxsize), void *context, uint32_t data_size ); Remove: vchi_msg_queuev_ex vchi_msg_queuev These functions were not implemented anyway so no need to fix them. It's easier to just remove them. Signed-off-by: Michael Zoran <mzoran@crowfest.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
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.c103
1 files changed, 102 insertions, 1 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 4e0401e1b1d0..20bdc7d89255 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -402,6 +402,107 @@ static void close_delivered(USER_SERVICE_T *user_service)
}
}
+struct vchiq_io_copy_callback_context {
+ VCHIQ_ELEMENT_T *current_element;
+ size_t current_element_offset;
+ unsigned long elements_to_go;
+ size_t current_offset;
+};
+
+static ssize_t
+vchiq_ioc_copy_element_data(
+ void *context,
+ void *dest,
+ size_t offset,
+ size_t maxsize)
+{
+ long res;
+ size_t bytes_this_round;
+ struct vchiq_io_copy_callback_context *copy_context =
+ (struct vchiq_io_copy_callback_context *)context;
+
+ if (offset != copy_context->current_offset)
+ return 0;
+
+ if (!copy_context->elements_to_go)
+ return 0;
+
+ /*
+ * Complex logic here to handle the case of 0 size elements
+ * in the middle of the array of elements.
+ *
+ * Need to skip over these 0 size elements.
+ */
+ while (1) {
+ bytes_this_round = min(copy_context->current_element->size -
+ copy_context->current_element_offset,
+ maxsize);
+
+ if (bytes_this_round)
+ break;
+
+ copy_context->elements_to_go--;
+ copy_context->current_element++;
+ copy_context->current_element_offset = 0;
+
+ if (!copy_context->elements_to_go)
+ return 0;
+ }
+
+ res = copy_from_user(dest,
+ copy_context->current_element->data +
+ copy_context->current_element_offset,
+ bytes_this_round);
+
+ if (res != 0)
+ return -EFAULT;
+
+ copy_context->current_element_offset += bytes_this_round;
+ copy_context->current_offset += bytes_this_round;
+
+ /*
+ * Check if done with current element, and if so advance to the next.
+ */
+ if (copy_context->current_element_offset ==
+ copy_context->current_element->size) {
+ copy_context->elements_to_go--;
+ copy_context->current_element++;
+ copy_context->current_element_offset = 0;
+ }
+
+ return bytes_this_round;
+}
+
+/**************************************************************************
+ *
+ * vchiq_ioc_queue_message
+ *
+ **************************************************************************/
+static VCHIQ_STATUS_T
+vchiq_ioc_queue_message(VCHIQ_SERVICE_HANDLE_T handle,
+ VCHIQ_ELEMENT_T *elements,
+ unsigned long count)
+{
+ struct vchiq_io_copy_callback_context context;
+ unsigned long i;
+ size_t total_size = 0;
+
+ context.current_element = elements;
+ context.current_element_offset = 0;
+ context.elements_to_go = count;
+ context.current_offset = 0;
+
+ for (i = 0; i < count; i++) {
+ if (!elements[i].data && elements[i].size != 0)
+ return -EFAULT;
+
+ total_size += elements[i].size;
+ }
+
+ return vchiq_queue_message(handle, vchiq_ioc_copy_element_data,
+ &context, total_size);
+}
+
/****************************************************************************
*
* vchiq_ioctl
@@ -651,7 +752,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
VCHIQ_ELEMENT_T elements[MAX_ELEMENTS];
if (copy_from_user(elements, args.elements,
args.count * sizeof(VCHIQ_ELEMENT_T)) == 0)
- status = vchiq_queue_message
+ status = vchiq_ioc_queue_message
(args.handle,
elements, args.count);
else