diff options
| author | Thomas Zimmermann <tzimmermann@suse.de> | 2023-04-28 14:24:51 +0200 |
|---|---|---|
| committer | Thomas Zimmermann <tzimmermann@suse.de> | 2023-05-08 15:28:34 +0200 |
| commit | 6121cd9ef911432b14c2a17aefaf8cd2f3cfcdff (patch) | |
| tree | 6db707c245921b6460cbda1c20f22e1a737f7f59 /drivers/video/fbdev/core/fb_io_fops.c | |
| parent | fbdev: Validate info->screen_{base, buffer} in fb_ops implementations (diff) | |
| download | linux-6121cd9ef911432b14c2a17aefaf8cd2f3cfcdff.tar.gz linux-6121cd9ef911432b14c2a17aefaf8cd2f3cfcdff.zip | |
fbdev: Move I/O read and write code into helper functions
Move the existing I/O read and write code for I/O memory into
the new helpers fb_cfb_read() and fb_cfb_write(). Make them the
default fp_ops. No functional changes.
In the near term, the new functions will be useful to the DRM
subsystem, which currently provides it's own implementation. It
can then use the shared code. In the longer term, it might make
sense to revise the I/O helper's default status and make them
opt-in by the driver. Systems that don't use them would not
contain the code any longer.
v2:
* add detailed commit message (Javier)
* rename fb_cfb_() to fb_io_() (Geert)
* add fixes that got lost while moving the code (Geert)
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Tested-by: Sui Jingfeng <suijingfeng@loongson.cn>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Helge Deller <deller@gmx.de>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20230428122452.4856-19-tzimmermann@suse.de
Diffstat (limited to 'drivers/video/fbdev/core/fb_io_fops.c')
| -rw-r--r-- | drivers/video/fbdev/core/fb_io_fops.c | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/drivers/video/fbdev/core/fb_io_fops.c b/drivers/video/fbdev/core/fb_io_fops.c new file mode 100644 index 000000000000..f5299d50f33b --- /dev/null +++ b/drivers/video/fbdev/core/fb_io_fops.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/fb.h> +#include <linux/module.h> +#include <linux/uaccess.h> + +ssize_t fb_io_read(struct fb_info *info, char __user *buf, size_t count, loff_t *ppos) +{ + unsigned long p = *ppos; + u8 *buffer, *dst; + u8 __iomem *src; + int c, cnt = 0, err = 0; + unsigned long total_size, trailing; + + if (!info->screen_base) + return -ENODEV; + + total_size = info->screen_size; + + if (total_size == 0) + total_size = info->fix.smem_len; + + if (p >= total_size) + return 0; + + if (count >= total_size) + count = total_size; + + if (count + p > total_size) + count = total_size - p; + + buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, + GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + src = (u8 __iomem *) (info->screen_base + p); + + if (info->fbops->fb_sync) + info->fbops->fb_sync(info); + + while (count) { + c = (count > PAGE_SIZE) ? PAGE_SIZE : count; + dst = buffer; + fb_memcpy_fromfb(dst, src, c); + dst += c; + src += c; + + trailing = copy_to_user(buf, buffer, c); + if (trailing == c) { + err = -EFAULT; + break; + } + c -= trailing; + + *ppos += c; + buf += c; + cnt += c; + count -= c; + } + + kfree(buffer); + + return cnt ? cnt : err; +} +EXPORT_SYMBOL(fb_io_read); + +ssize_t fb_io_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos) +{ + unsigned long p = *ppos; + u8 *buffer, *src; + u8 __iomem *dst; + int c, cnt = 0, err = 0; + unsigned long total_size, trailing; + + if (!info->screen_base) + return -ENODEV; + + total_size = info->screen_size; + + if (total_size == 0) + total_size = info->fix.smem_len; + + if (p > total_size) + return -EFBIG; + + if (count > total_size) { + err = -EFBIG; + count = total_size; + } + + if (count + p > total_size) { + if (!err) + err = -ENOSPC; + + count = total_size - p; + } + + buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count, + GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + dst = (u8 __iomem *) (info->screen_base + p); + + if (info->fbops->fb_sync) + info->fbops->fb_sync(info); + + while (count) { + c = (count > PAGE_SIZE) ? PAGE_SIZE : count; + src = buffer; + + trailing = copy_from_user(src, buf, c); + if (trailing == c) { + err = -EFAULT; + break; + } + c -= trailing; + + fb_memcpy_tofb(dst, src, c); + dst += c; + src += c; + *ppos += c; + buf += c; + cnt += c; + count -= c; + } + + kfree(buffer); + + return (cnt) ? cnt : err; +} +EXPORT_SYMBOL(fb_io_write); |
