/**************************************************************************1*2* Copyright 2010 Pauli Nieminen.3* All Rights Reserved.4*5* Permission is hereby granted, free of charge, to any person obtaining a6* copy of this software and associated documentation files (the7* "Software"), to deal in the Software without restriction, including8* without limitation the rights to use, copy, modify, merge, publish,9* distribute, sub license, and/or sell copies of the Software, and to10* permit persons to whom the Software is furnished to do so, subject to11* the following conditions:12*13* The above copyright notice and this permission notice (including the14* next paragraph) shall be included in all copies or substantial portions15* of the Software.16*17* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR18* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,19* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL20* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,21* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR22* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE23* USE OR OTHER DEALINGS IN THE SOFTWARE.24*25*26**************************************************************************/27/*28* Multipart buffer for coping data which is larger than the page size.29*30* Authors:31* Pauli Nieminen <suokkos-at-gmail-dot-com>32*/3334#include "drm_buffer.h"3536/**37* Allocate the drm buffer object.38*39* buf: Pointer to a pointer where the object is stored.40* size: The number of bytes to allocate.41*/42int drm_buffer_alloc(struct drm_buffer **buf, int size)43{44int nr_pages = size / PAGE_SIZE + 1;45int idx;4647/* Allocating pointer table to end of structure makes drm_buffer48* variable sized */49*buf = kzalloc(sizeof(struct drm_buffer) + nr_pages*sizeof(char *),50GFP_KERNEL);5152if (*buf == NULL) {53DRM_ERROR("Failed to allocate drm buffer object to hold"54" %d bytes in %d pages.\n",55size, nr_pages);56return -ENOMEM;57}5859(*buf)->size = size;6061for (idx = 0; idx < nr_pages; ++idx) {6263(*buf)->data[idx] =64kmalloc(min(PAGE_SIZE, size - idx * PAGE_SIZE),65GFP_KERNEL);666768if ((*buf)->data[idx] == NULL) {69DRM_ERROR("Failed to allocate %dth page for drm"70" buffer with %d bytes and %d pages.\n",71idx + 1, size, nr_pages);72goto error_out;73}7475}7677return 0;7879error_out:8081/* Only last element can be null pointer so check for it first. */82if ((*buf)->data[idx])83kfree((*buf)->data[idx]);8485for (--idx; idx >= 0; --idx)86kfree((*buf)->data[idx]);8788kfree(*buf);89return -ENOMEM;90}91EXPORT_SYMBOL(drm_buffer_alloc);9293/**94* Copy the user data to the begin of the buffer and reset the processing95* iterator.96*97* user_data: A pointer the data that is copied to the buffer.98* size: The Number of bytes to copy.99*/100int drm_buffer_copy_from_user(struct drm_buffer *buf,101void __user *user_data, int size)102{103int nr_pages = size / PAGE_SIZE + 1;104int idx;105106if (size > buf->size) {107DRM_ERROR("Requesting to copy %d bytes to a drm buffer with"108" %d bytes space\n",109size, buf->size);110return -EFAULT;111}112113for (idx = 0; idx < nr_pages; ++idx) {114115if (DRM_COPY_FROM_USER(buf->data[idx],116user_data + idx * PAGE_SIZE,117min(PAGE_SIZE, size - idx * PAGE_SIZE))) {118DRM_ERROR("Failed to copy user data (%p) to drm buffer"119" (%p) %dth page.\n",120user_data, buf, idx);121return -EFAULT;122123}124}125buf->iterator = 0;126return 0;127}128EXPORT_SYMBOL(drm_buffer_copy_from_user);129130/**131* Free the drm buffer object132*/133void drm_buffer_free(struct drm_buffer *buf)134{135136if (buf != NULL) {137138int nr_pages = buf->size / PAGE_SIZE + 1;139int idx;140for (idx = 0; idx < nr_pages; ++idx)141kfree(buf->data[idx]);142143kfree(buf);144}145}146EXPORT_SYMBOL(drm_buffer_free);147148/**149* Read an object from buffer that may be split to multiple parts. If object150* is not split function just returns the pointer to object in buffer. But in151* case of split object data is copied to given stack object that is suplied152* by caller.153*154* The processing location of the buffer is also advanced to the next byte155* after the object.156*157* objsize: The size of the objet in bytes.158* stack_obj: A pointer to a memory location where object can be copied.159*/160void *drm_buffer_read_object(struct drm_buffer *buf,161int objsize, void *stack_obj)162{163int idx = drm_buffer_index(buf);164int page = drm_buffer_page(buf);165void *obj = NULL;166167if (idx + objsize <= PAGE_SIZE) {168obj = &buf->data[page][idx];169} else {170/* The object is split which forces copy to temporary object.*/171int beginsz = PAGE_SIZE - idx;172memcpy(stack_obj, &buf->data[page][idx], beginsz);173174memcpy(stack_obj + beginsz, &buf->data[page + 1][0],175objsize - beginsz);176177obj = stack_obj;178}179180drm_buffer_advance(buf, objsize);181return obj;182}183EXPORT_SYMBOL(drm_buffer_read_object);184185186