aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/vmwgfx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/vmwgfx')
-rw-r--r--drivers/gpu/drm/vmwgfx/Makefile2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c6
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c142
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.h50
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c25
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h3
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c109
7 files changed, 221 insertions, 116 deletions
diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile
index 09f6dcac768b..bc323f7d4032 100644
--- a/drivers/gpu/drm/vmwgfx/Makefile
+++ b/drivers/gpu/drm/vmwgfx/Makefile
@@ -9,7 +9,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
vmwgfx_cotable.o vmwgfx_so.o vmwgfx_binding.o vmwgfx_msg.o \
vmwgfx_simple_resource.o vmwgfx_va.o vmwgfx_blit.o \
vmwgfx_validation.o vmwgfx_page_dirty.o vmwgfx_streamoutput.o \
- ttm_object.o ttm_memory.o
+ vmwgfx_devcaps.o ttm_object.o ttm_memory.o
vmwgfx-$(CONFIG_DRM_FBDEV_EMULATION) += vmwgfx_fb.o
vmwgfx-$(CONFIG_TRANSPARENT_HUGEPAGE) += vmwgfx_thp.o
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c
index 956b85e35cef..30a837b5baa6 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c
@@ -30,6 +30,7 @@
#include <drm/ttm/ttm_placement.h>
#include "vmwgfx_drv.h"
+#include "vmwgfx_devcaps.h"
bool vmw_supports_3d(struct vmw_private *dev_priv)
{
@@ -45,10 +46,7 @@ bool vmw_supports_3d(struct vmw_private *dev_priv)
if (!dev_priv->has_mob)
return false;
- spin_lock(&dev_priv->cap_lock);
- vmw_write(dev_priv, SVGA_REG_DEV_CAP, SVGA3D_DEVCAP_3D);
- result = vmw_read(dev_priv, SVGA_REG_DEV_CAP);
- spin_unlock(&dev_priv->cap_lock);
+ result = vmw_devcap_get(dev_priv, SVGA3D_DEVCAP_3D);
return (result != 0);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c b/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c
new file mode 100644
index 000000000000..04fc67d53563
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.c
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/**************************************************************************
+ *
+ * Copyright 2021 VMware, Inc., Palo Alto, CA., USA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#include "vmwgfx_devcaps.h"
+
+#include "vmwgfx_drv.h"
+
+
+struct svga_3d_compat_cap {
+ SVGA3dCapsRecordHeader header;
+ SVGA3dCapPair pairs[SVGA3D_DEVCAP_MAX];
+};
+
+
+static u32 vmw_mask_legacy_multisample(unsigned int cap, u32 fmt_value)
+{
+ /*
+ * A version of user-space exists which use MULTISAMPLE_MASKABLESAMPLES
+ * to check the sample count supported by virtual device. Since there
+ * never was support for multisample count for backing MOB return 0.
+ *
+ * MULTISAMPLE_MASKABLESAMPLES devcap is marked as deprecated by virtual
+ * device.
+ */
+ if (cap == SVGA3D_DEVCAP_DEAD5)
+ return 0;
+
+ return fmt_value;
+}
+
+static int vmw_fill_compat_cap(struct vmw_private *dev_priv, void *bounce,
+ size_t size)
+{
+ struct svga_3d_compat_cap *compat_cap =
+ (struct svga_3d_compat_cap *) bounce;
+ unsigned int i;
+ size_t pair_offset = offsetof(struct svga_3d_compat_cap, pairs);
+ unsigned int max_size;
+
+ if (size < pair_offset)
+ return -EINVAL;
+
+ max_size = (size - pair_offset) / sizeof(SVGA3dCapPair);
+
+ if (max_size > SVGA3D_DEVCAP_MAX)
+ max_size = SVGA3D_DEVCAP_MAX;
+
+ compat_cap->header.length =
+ (pair_offset + max_size * sizeof(SVGA3dCapPair)) / sizeof(u32);
+ compat_cap->header.type = SVGA3DCAPS_RECORD_DEVCAPS;
+
+ for (i = 0; i < max_size; ++i) {
+ compat_cap->pairs[i][0] = i;
+ compat_cap->pairs[i][1] = vmw_mask_legacy_multisample
+ (i, dev_priv->devcaps[i]);
+ }
+
+ return 0;
+}
+
+int vmw_devcaps_create(struct vmw_private *vmw)
+{
+ bool gb_objects = !!(vmw->capabilities & SVGA_CAP_GBOBJECTS);
+ uint32_t i;
+
+ if (gb_objects) {
+ vmw->devcaps = vzalloc(sizeof(uint32_t) * SVGA3D_DEVCAP_MAX);
+ if (!vmw->devcaps)
+ return -ENOMEM;
+ for (i = 0; i < SVGA3D_DEVCAP_MAX; ++i) {
+ vmw_write(vmw, SVGA_REG_DEV_CAP, i);
+ vmw->devcaps[i] = vmw_read(vmw, SVGA_REG_DEV_CAP);
+ }
+ }
+ return 0;
+}
+
+void vmw_devcaps_destroy(struct vmw_private *vmw)
+{
+ vfree(vmw->devcaps);
+ vmw->devcaps = NULL;
+}
+
+
+uint32 vmw_devcaps_size(const struct vmw_private *vmw,
+ bool gb_aware)
+{
+ bool gb_objects = !!(vmw->capabilities & SVGA_CAP_GBOBJECTS);
+ if (gb_objects && gb_aware)
+ return SVGA3D_DEVCAP_MAX * sizeof(uint32_t);
+ else if (gb_objects)
+ return sizeof(struct svga_3d_compat_cap) +
+ sizeof(uint32_t);
+ else if (vmw->fifo_mem != NULL)
+ return (SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1) *
+ sizeof(uint32_t);
+ else
+ return 0;
+}
+
+int vmw_devcaps_copy(struct vmw_private *vmw, bool gb_aware,
+ void *dst, uint32_t dst_size)
+{
+ int ret;
+ bool gb_objects = !!(vmw->capabilities & SVGA_CAP_GBOBJECTS);
+ if (gb_objects && gb_aware) {
+ memcpy(dst, vmw->devcaps, dst_size);
+ } else if (gb_objects) {
+ ret = vmw_fill_compat_cap(vmw, dst, dst_size);
+ if (unlikely(ret != 0))
+ return ret;
+ } else if (vmw->fifo_mem) {
+ u32 *fifo_mem = vmw->fifo_mem;
+ memcpy(dst, &fifo_mem[SVGA_FIFO_3D_CAPS], dst_size);
+ } else
+ return -EINVAL;
+ return 0;
+}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.h b/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.h
new file mode 100644
index 000000000000..b7c43e5f07c3
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_devcaps.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/**************************************************************************
+ *
+ * Copyright 2021 VMware, Inc., Palo Alto, CA., USA
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+
+#ifndef _VMWGFX_DEVCAPS_H_
+#define _VMWGFX_DEVCAPS_H_
+
+#include "vmwgfx_drv.h"
+
+#include "device_include/svga3d_caps.h"
+
+int vmw_devcaps_create(struct vmw_private *vmw);
+void vmw_devcaps_destroy(struct vmw_private *vmw);
+uint32_t vmw_devcaps_size(const struct vmw_private *vmw, bool gb_aware);
+int vmw_devcaps_copy(struct vmw_private *vmw, bool gb_aware,
+ void *dst, uint32_t dst_size);
+
+static inline uint32_t vmw_devcap_get(struct vmw_private *vmw,
+ uint32_t devcap)
+{
+ bool gb_objects = !!(vmw->capabilities & SVGA_CAP_GBOBJECTS);
+ if (gb_objects)
+ return vmw->devcaps[devcap];
+ return 0;
+}
+
+#endif
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 6f5ea00973e0..3e438de0f157 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -41,6 +41,7 @@
#include "ttm_object.h"
#include "vmwgfx_binding.h"
+#include "vmwgfx_devcaps.h"
#include "vmwgfx_drv.h"
#define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices"
@@ -792,7 +793,6 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id)
spin_lock_init(&dev_priv->resource_lock);
spin_lock_init(&dev_priv->hw_lock);
spin_lock_init(&dev_priv->waiter_lock);
- spin_lock_init(&dev_priv->cap_lock);
spin_lock_init(&dev_priv->cursor_lock);
ret = vmw_setup_pci_resources(dev_priv, pci_id);
@@ -982,6 +982,12 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id)
goto out_no_vram;
}
+ ret = vmw_devcaps_create(dev_priv);
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Failed initializing device caps.\n");
+ goto out_no_vram;
+ }
+
/*
* "Guest Memory Regions" is an aperture like feature with
* one slot per bo. There is an upper limit of the number of
@@ -1008,11 +1014,8 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id)
}
if (dev_priv->has_mob && (dev_priv->capabilities & SVGA_CAP_DX)) {
- spin_lock(&dev_priv->cap_lock);
- vmw_write(dev_priv, SVGA_REG_DEV_CAP, SVGA3D_DEVCAP_DXCONTEXT);
- if (vmw_read(dev_priv, SVGA_REG_DEV_CAP))
+ if (vmw_devcap_get(dev_priv, SVGA3D_DEVCAP_DXCONTEXT))
dev_priv->sm_type = VMW_SM_4;
- spin_unlock(&dev_priv->cap_lock);
}
vmw_validation_mem_init_ttm(dev_priv, VMWGFX_VALIDATION_MEM_GRAN);
@@ -1020,15 +1023,11 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id)
/* SVGA_CAP2_DX2 (DefineGBSurface_v3) is needed for SM4_1 support */
if (has_sm4_context(dev_priv) &&
(dev_priv->capabilities2 & SVGA_CAP2_DX2)) {
- vmw_write(dev_priv, SVGA_REG_DEV_CAP, SVGA3D_DEVCAP_SM41);
-
- if (vmw_read(dev_priv, SVGA_REG_DEV_CAP))
+ if (vmw_devcap_get(dev_priv, SVGA3D_DEVCAP_SM41))
dev_priv->sm_type = VMW_SM_4_1;
-
if (has_sm4_1_context(dev_priv) &&
- (dev_priv->capabilities2 & SVGA_CAP2_DX3)) {
- vmw_write(dev_priv, SVGA_REG_DEV_CAP, SVGA3D_DEVCAP_SM5);
- if (vmw_read(dev_priv, SVGA_REG_DEV_CAP))
+ (dev_priv->capabilities2 & SVGA_CAP2_DX3)) {
+ if (vmw_devcap_get(dev_priv, SVGA3D_DEVCAP_SM5))
dev_priv->sm_type = VMW_SM_5;
}
}
@@ -1073,6 +1072,7 @@ out_no_kms:
vmw_gmrid_man_fini(dev_priv, VMW_PL_MOB);
if (dev_priv->has_gmr)
vmw_gmrid_man_fini(dev_priv, VMW_PL_GMR);
+ vmw_devcaps_destroy(dev_priv);
vmw_vram_manager_fini(dev_priv);
out_no_vram:
ttm_device_fini(&dev_priv->bdev);
@@ -1121,6 +1121,7 @@ static void vmw_driver_unload(struct drm_device *dev)
vmw_release_device_early(dev_priv);
if (dev_priv->has_mob)
vmw_gmrid_man_fini(dev_priv, VMW_PL_MOB);
+ vmw_devcaps_destroy(dev_priv);
vmw_vram_manager_fini(dev_priv);
ttm_device_fini(&dev_priv->bdev);
drm_vma_offset_manager_destroy(&dev_priv->vma_manager);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index d1cef3b69e9d..4c2afe9c0505 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -513,7 +513,6 @@ struct vmw_private {
bool has_gmr;
bool has_mob;
spinlock_t hw_lock;
- spinlock_t cap_lock;
bool assume_16bpp;
enum vmw_sm_type sm_type;
@@ -629,6 +628,8 @@ struct vmw_private {
/* Validation memory reservation */
struct vmw_validation_mem vvm;
+
+ uint32 *devcaps;
};
static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
index 4fdacf9924e6..c34f61ac4ce4 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
@@ -26,14 +26,9 @@
**************************************************************************/
#include "vmwgfx_drv.h"
+#include "vmwgfx_devcaps.h"
#include <drm/vmwgfx_drm.h>
#include "vmwgfx_kms.h"
-#include "device_include/svga3d_caps.h"
-
-struct svga_3d_compat_cap {
- SVGA3dCapsRecordHeader header;
- SVGA3dCapPair pairs[SVGA3D_DEVCAP_MAX];
-};
int vmw_getparam_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
@@ -88,16 +83,7 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,
param->value = dev_priv->memory_size;
break;
case DRM_VMW_PARAM_3D_CAPS_SIZE:
- if ((dev_priv->capabilities & SVGA_CAP_GBOBJECTS) &&
- vmw_fp->gb_aware)
- param->value = SVGA3D_DEVCAP_MAX * sizeof(uint32_t);
- else if (dev_priv->capabilities & SVGA_CAP_GBOBJECTS)
- param->value = sizeof(struct svga_3d_compat_cap) +
- sizeof(uint32_t);
- else
- param->value = (SVGA_FIFO_3D_CAPS_LAST -
- SVGA_FIFO_3D_CAPS + 1) *
- sizeof(uint32_t);
+ param->value = vmw_devcaps_size(dev_priv, vmw_fp->gb_aware);
break;
case DRM_VMW_PARAM_MAX_MOB_MEMORY:
vmw_fp->gb_aware = true;
@@ -126,55 +112,6 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data,
return 0;
}
-static u32 vmw_mask_legacy_multisample(unsigned int cap, u32 fmt_value)
-{
- /*
- * A version of user-space exists which use MULTISAMPLE_MASKABLESAMPLES
- * to check the sample count supported by virtual device. Since there
- * never was support for multisample count for backing MOB return 0.
- *
- * MULTISAMPLE_MASKABLESAMPLES devcap is marked as deprecated by virtual
- * device.
- */
- if (cap == SVGA3D_DEVCAP_DEAD5)
- return 0;
-
- return fmt_value;
-}
-
-static int vmw_fill_compat_cap(struct vmw_private *dev_priv, void *bounce,
- size_t size)
-{
- struct svga_3d_compat_cap *compat_cap =
- (struct svga_3d_compat_cap *) bounce;
- unsigned int i;
- size_t pair_offset = offsetof(struct svga_3d_compat_cap, pairs);
- unsigned int max_size;
-
- if (size < pair_offset)
- return -EINVAL;
-
- max_size = (size - pair_offset) / sizeof(SVGA3dCapPair);
-
- if (max_size > SVGA3D_DEVCAP_MAX)
- max_size = SVGA3D_DEVCAP_MAX;
-
- compat_cap->header.length =
- (pair_offset + max_size * sizeof(SVGA3dCapPair)) / sizeof(u32);
- compat_cap->header.type = SVGA3DCAPS_RECORD_DEVCAPS;
-
- spin_lock(&dev_priv->cap_lock);
- for (i = 0; i < max_size; ++i) {
- vmw_write(dev_priv, SVGA_REG_DEV_CAP, i);
- compat_cap->pairs[i][0] = i;
- compat_cap->pairs[i][1] = vmw_mask_legacy_multisample
- (i, vmw_read(dev_priv, SVGA_REG_DEV_CAP));
- }
- spin_unlock(&dev_priv->cap_lock);
-
- return 0;
-}
-
int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
@@ -183,11 +120,9 @@ int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
(struct drm_vmw_get_3d_cap_arg *) data;
struct vmw_private *dev_priv = vmw_priv(dev);
uint32_t size;
- u32 *fifo_mem;
void __user *buffer = (void __user *)((unsigned long)(arg->buffer));
- void *bounce;
+ void *bounce = NULL;
int ret;
- bool gb_objects = !!(dev_priv->capabilities & SVGA_CAP_GBOBJECTS);
struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
if (unlikely(arg->pad64 != 0 || arg->max_size == 0)) {
@@ -195,13 +130,11 @@ int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
return -EINVAL;
}
- if (gb_objects && vmw_fp->gb_aware)
- size = SVGA3D_DEVCAP_MAX * sizeof(uint32_t);
- else if (gb_objects)
- size = sizeof(struct svga_3d_compat_cap) + sizeof(uint32_t);
- else
- size = (SVGA_FIFO_3D_CAPS_LAST - SVGA_FIFO_3D_CAPS + 1) *
- sizeof(uint32_t);
+ size = vmw_devcaps_size(dev_priv, vmw_fp->gb_aware);
+ if (unlikely(size == 0)) {
+ DRM_ERROR("Failed to figure out the devcaps size (no 3D).\n");
+ return -ENOMEM;
+ }
if (arg->max_size < size)
size = arg->max_size;
@@ -212,29 +145,9 @@ int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data,
return -ENOMEM;
}
- if (gb_objects && vmw_fp->gb_aware) {
- int i, num;
- uint32_t *bounce32 = (uint32_t *) bounce;
-
- num = size / sizeof(uint32_t);
- if (num > SVGA3D_DEVCAP_MAX)
- num = SVGA3D_DEVCAP_MAX;
-
- spin_lock(&dev_priv->cap_lock);
- for (i = 0; i < num; ++i) {
- vmw_write(dev_priv, SVGA_REG_DEV_CAP, i);
- *bounce32++ = vmw_mask_legacy_multisample
- (i, vmw_read(dev_priv, SVGA_REG_DEV_CAP));
- }
- spin_unlock(&dev_priv->cap_lock);
- } else if (gb_objects) {
- ret = vmw_fill_compat_cap(dev_priv, bounce, size);
- if (unlikely(ret != 0))
- goto out_err;
- } else {
- fifo_mem = dev_priv->fifo_mem;
- memcpy(bounce, &fifo_mem[SVGA_FIFO_3D_CAPS], size);
- }
+ ret = vmw_devcaps_copy(dev_priv, vmw_fp->gb_aware, bounce, size);
+ if (unlikely (ret != 0))
+ goto out_err;
ret = copy_to_user(buffer, bounce, size);
if (ret)