123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957 |
- /*
- * Copyright (C) 2013 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #include <linux/bitops.h>
- #include <linux/circ_buf.h>
- #include <linux/fs.h>
- #include <linux/module.h>
- #include <linux/poll.h>
- #include <linux/slab.h>
- #include <linux/uaccess.h>
- #include <video/adf_client.h>
- #include <video/adf_format.h>
- #include "sw_sync.h"
- #include "sync.h"
- #include "adf.h"
- #include "adf_fops.h"
- #include "adf_sysfs.h"
- #ifdef CONFIG_COMPAT
- #include "adf_fops32.h"
- #endif
- static int adf_obj_set_event(struct adf_obj *obj, struct adf_file *file,
- struct adf_set_event __user *arg)
- {
- struct adf_set_event data;
- bool enabled;
- unsigned long flags;
- int err;
- if (copy_from_user(&data, arg, sizeof(data)))
- return -EFAULT;
- err = adf_obj_check_supports_event(obj, data.type);
- if (err < 0)
- return err;
- spin_lock_irqsave(&obj->file_lock, flags);
- if (data.enabled)
- enabled = test_and_set_bit(data.type,
- file->event_subscriptions);
- else
- enabled = test_and_clear_bit(data.type,
- file->event_subscriptions);
- spin_unlock_irqrestore(&obj->file_lock, flags);
- if (data.enabled == enabled)
- return -EALREADY;
- if (data.enabled)
- adf_event_get(obj, data.type);
- else
- adf_event_put(obj, data.type);
- return 0;
- }
- static int adf_obj_copy_custom_data_to_user(struct adf_obj *obj,
- void __user *dst, size_t *dst_size)
- {
- void *custom_data;
- size_t custom_data_size;
- int ret;
- if (!obj->ops || !obj->ops->custom_data) {
- dev_dbg(&obj->dev, "%s: no custom_data op\n", __func__);
- return 0;
- }
- custom_data = kzalloc(ADF_MAX_CUSTOM_DATA_SIZE, GFP_KERNEL);
- if (!custom_data)
- return -ENOMEM;
- ret = obj->ops->custom_data(obj, custom_data, &custom_data_size);
- if (ret < 0)
- goto done;
- if (copy_to_user(dst, custom_data, min(*dst_size, custom_data_size))) {
- ret = -EFAULT;
- goto done;
- }
- *dst_size = custom_data_size;
- done:
- kfree(custom_data);
- return ret;
- }
- static int adf_eng_get_data(struct adf_overlay_engine *eng,
- struct adf_overlay_engine_data __user *arg)
- {
- struct adf_device *dev = adf_overlay_engine_parent(eng);
- struct adf_overlay_engine_data data;
- size_t n_supported_formats;
- u32 *supported_formats = NULL;
- int ret = 0;
- if (copy_from_user(&data, arg, sizeof(data)))
- return -EFAULT;
- strlcpy(data.name, eng->base.name, sizeof(data.name));
- if (data.n_supported_formats > ADF_MAX_SUPPORTED_FORMATS)
- return -EINVAL;
- n_supported_formats = data.n_supported_formats;
- data.n_supported_formats = eng->ops->n_supported_formats;
- if (n_supported_formats) {
- supported_formats = kzalloc(n_supported_formats *
- sizeof(supported_formats[0]), GFP_KERNEL);
- if (!supported_formats)
- return -ENOMEM;
- }
- memcpy(supported_formats, eng->ops->supported_formats,
- sizeof(u32) * min(n_supported_formats,
- eng->ops->n_supported_formats));
- mutex_lock(&dev->client_lock);
- ret = adf_obj_copy_custom_data_to_user(&eng->base, arg->custom_data,
- &data.custom_data_size);
- mutex_unlock(&dev->client_lock);
- if (ret < 0)
- goto done;
- if (copy_to_user(arg, &data, sizeof(data))) {
- ret = -EFAULT;
- goto done;
- }
- if (supported_formats && copy_to_user(arg->supported_formats,
- supported_formats,
- n_supported_formats * sizeof(supported_formats[0])))
- ret = -EFAULT;
- done:
- kfree(supported_formats);
- return ret;
- }
- static int adf_buffer_import(struct adf_device *dev,
- struct adf_buffer_config __user *cfg, struct adf_buffer *buf)
- {
- struct adf_buffer_config user_buf;
- size_t i;
- int ret = 0;
- if (copy_from_user(&user_buf, cfg, sizeof(user_buf)))
- return -EFAULT;
- memset(buf, 0, sizeof(*buf));
- if (user_buf.n_planes > ADF_MAX_PLANES) {
- dev_err(&dev->base.dev, "invalid plane count %u\n",
- user_buf.n_planes);
- return -EINVAL;
- }
- buf->overlay_engine = idr_find(&dev->overlay_engines,
- user_buf.overlay_engine);
- if (!buf->overlay_engine) {
- dev_err(&dev->base.dev, "invalid overlay engine id %u\n",
- user_buf.overlay_engine);
- return -ENOENT;
- }
- buf->w = user_buf.w;
- buf->h = user_buf.h;
- buf->format = user_buf.format;
- for (i = 0; i < user_buf.n_planes; i++) {
- buf->dma_bufs[i] = dma_buf_get(user_buf.fd[i]);
- if (IS_ERR(buf->dma_bufs[i])) {
- ret = PTR_ERR(buf->dma_bufs[i]);
- dev_err(&dev->base.dev, "importing dma_buf fd %d failed: %d\n",
- user_buf.fd[i], ret);
- buf->dma_bufs[i] = NULL;
- goto done;
- }
- buf->offset[i] = user_buf.offset[i];
- buf->pitch[i] = user_buf.pitch[i];
- }
- buf->n_planes = user_buf.n_planes;
- if (user_buf.acquire_fence >= 0) {
- buf->acquire_fence = sync_fence_fdget(user_buf.acquire_fence);
- if (!buf->acquire_fence) {
- dev_err(&dev->base.dev, "getting fence fd %d failed\n",
- user_buf.acquire_fence);
- ret = -EINVAL;
- goto done;
- }
- }
- done:
- if (ret < 0)
- adf_buffer_cleanup(buf);
- return ret;
- }
- static int adf_device_post_config(struct adf_device *dev,
- struct adf_post_config __user *arg)
- {
- struct sync_fence *complete_fence;
- int complete_fence_fd;
- struct adf_buffer *bufs = NULL;
- struct adf_interface **intfs = NULL;
- size_t n_intfs, n_bufs, i;
- void *custom_data = NULL;
- size_t custom_data_size;
- int ret = 0;
- complete_fence_fd = get_unused_fd_flags(O_CLOEXEC);
- if (complete_fence_fd < 0)
- return complete_fence_fd;
- if (get_user(n_intfs, &arg->n_interfaces)) {
- ret = -EFAULT;
- goto err_get_user;
- }
- if (n_intfs > ADF_MAX_INTERFACES) {
- ret = -EINVAL;
- goto err_get_user;
- }
- if (get_user(n_bufs, &arg->n_bufs)) {
- ret = -EFAULT;
- goto err_get_user;
- }
- if (n_bufs > ADF_MAX_BUFFERS) {
- ret = -EINVAL;
- goto err_get_user;
- }
- if (get_user(custom_data_size, &arg->custom_data_size)) {
- ret = -EFAULT;
- goto err_get_user;
- }
- if (custom_data_size > ADF_MAX_CUSTOM_DATA_SIZE) {
- ret = -EINVAL;
- goto err_get_user;
- }
- if (n_intfs) {
- intfs = kmalloc(sizeof(intfs[0]) * n_intfs, GFP_KERNEL);
- if (!intfs) {
- ret = -ENOMEM;
- goto err_get_user;
- }
- }
- for (i = 0; i < n_intfs; i++) {
- u32 intf_id;
- if (get_user(intf_id, &arg->interfaces[i])) {
- ret = -EFAULT;
- goto err_get_user;
- }
- intfs[i] = idr_find(&dev->interfaces, intf_id);
- if (!intfs[i]) {
- ret = -EINVAL;
- goto err_get_user;
- }
- }
- if (n_bufs) {
- bufs = kzalloc(sizeof(bufs[0]) * n_bufs, GFP_KERNEL);
- if (!bufs) {
- ret = -ENOMEM;
- goto err_get_user;
- }
- }
- for (i = 0; i < n_bufs; i++) {
- ret = adf_buffer_import(dev, &arg->bufs[i], &bufs[i]);
- if (ret < 0) {
- memset(&bufs[i], 0, sizeof(bufs[i]));
- goto err_import;
- }
- }
- if (custom_data_size) {
- custom_data = kzalloc(custom_data_size, GFP_KERNEL);
- if (!custom_data) {
- ret = -ENOMEM;
- goto err_import;
- }
- if (copy_from_user(custom_data, arg->custom_data,
- custom_data_size)) {
- ret = -EFAULT;
- goto err_import;
- }
- }
- if (put_user(complete_fence_fd, &arg->complete_fence)) {
- ret = -EFAULT;
- goto err_import;
- }
- complete_fence = adf_device_post_nocopy(dev, intfs, n_intfs, bufs,
- n_bufs, custom_data, custom_data_size);
- if (IS_ERR(complete_fence)) {
- ret = PTR_ERR(complete_fence);
- goto err_import;
- }
- sync_fence_install(complete_fence, complete_fence_fd);
- return 0;
- err_import:
- for (i = 0; i < n_bufs; i++)
- adf_buffer_cleanup(&bufs[i]);
- err_get_user:
- kfree(custom_data);
- kfree(bufs);
- kfree(intfs);
- put_unused_fd(complete_fence_fd);
- return ret;
- }
- static int adf_intf_simple_post_config(struct adf_interface *intf,
- struct adf_simple_post_config __user *arg)
- {
- struct adf_device *dev = intf->base.parent;
- struct sync_fence *complete_fence;
- int complete_fence_fd;
- struct adf_buffer buf;
- int ret = 0;
- complete_fence_fd = get_unused_fd_flags(O_CLOEXEC);
- if (complete_fence_fd < 0)
- return complete_fence_fd;
- ret = adf_buffer_import(dev, &arg->buf, &buf);
- if (ret < 0)
- goto err_import;
- if (put_user(complete_fence_fd, &arg->complete_fence)) {
- ret = -EFAULT;
- goto err_put_user;
- }
- complete_fence = adf_interface_simple_post(intf, &buf);
- if (IS_ERR(complete_fence)) {
- ret = PTR_ERR(complete_fence);
- goto err_put_user;
- }
- sync_fence_install(complete_fence, complete_fence_fd);
- return 0;
- err_put_user:
- adf_buffer_cleanup(&buf);
- err_import:
- put_unused_fd(complete_fence_fd);
- return ret;
- }
- static int adf_intf_simple_buffer_alloc(struct adf_interface *intf,
- struct adf_simple_buffer_alloc __user *arg)
- {
- struct adf_simple_buffer_alloc data;
- struct dma_buf *dma_buf;
- int ret = 0;
- if (copy_from_user(&data, arg, sizeof(data)))
- return -EFAULT;
- data.fd = get_unused_fd_flags(O_CLOEXEC);
- if (data.fd < 0)
- return data.fd;
- ret = adf_interface_simple_buffer_alloc(intf, data.w, data.h,
- data.format, &dma_buf, &data.offset, &data.pitch);
- if (ret < 0)
- goto err_alloc;
- if (copy_to_user(arg, &data, sizeof(*arg))) {
- ret = -EFAULT;
- goto err_copy;
- }
- fd_install(data.fd, dma_buf->file);
- return 0;
- err_copy:
- dma_buf_put(dma_buf);
- err_alloc:
- put_unused_fd(data.fd);
- return ret;
- }
- static int adf_copy_attachment_list_to_user(
- struct adf_attachment_config __user *to, size_t n_to,
- struct adf_attachment *from, size_t n_from)
- {
- struct adf_attachment_config *temp;
- size_t n = min(n_to, n_from);
- size_t i;
- int ret = 0;
- if (!n)
- return 0;
- temp = kzalloc(n * sizeof(temp[0]), GFP_KERNEL);
- if (!temp)
- return -ENOMEM;
- for (i = 0; i < n; i++) {
- temp[i].interface = from[i].interface->base.id;
- temp[i].overlay_engine = from[i].overlay_engine->base.id;
- }
- if (copy_to_user(to, temp, n * sizeof(to[0]))) {
- ret = -EFAULT;
- goto done;
- }
- done:
- kfree(temp);
- return ret;
- }
- static int adf_device_get_data(struct adf_device *dev,
- struct adf_device_data __user *arg)
- {
- struct adf_device_data data;
- size_t n_attach;
- struct adf_attachment *attach = NULL;
- size_t n_allowed_attach;
- struct adf_attachment *allowed_attach = NULL;
- int ret = 0;
- if (copy_from_user(&data, arg, sizeof(data)))
- return -EFAULT;
- if (data.n_attachments > ADF_MAX_ATTACHMENTS ||
- data.n_allowed_attachments > ADF_MAX_ATTACHMENTS)
- return -EINVAL;
- strlcpy(data.name, dev->base.name, sizeof(data.name));
- if (data.n_attachments) {
- attach = kzalloc(data.n_attachments * sizeof(attach[0]),
- GFP_KERNEL);
- if (!attach)
- return -ENOMEM;
- }
- n_attach = adf_device_attachments(dev, attach, data.n_attachments);
- if (data.n_allowed_attachments) {
- allowed_attach = kzalloc(data.n_allowed_attachments *
- sizeof(allowed_attach[0]), GFP_KERNEL);
- if (!allowed_attach) {
- ret = -ENOMEM;
- goto done;
- }
- }
- n_allowed_attach = adf_device_attachments_allowed(dev, allowed_attach,
- data.n_allowed_attachments);
- mutex_lock(&dev->client_lock);
- ret = adf_obj_copy_custom_data_to_user(&dev->base, arg->custom_data,
- &data.custom_data_size);
- mutex_unlock(&dev->client_lock);
- if (ret < 0)
- goto done;
- ret = adf_copy_attachment_list_to_user(arg->attachments,
- data.n_attachments, attach, n_attach);
- if (ret < 0)
- goto done;
- ret = adf_copy_attachment_list_to_user(arg->allowed_attachments,
- data.n_allowed_attachments, allowed_attach,
- n_allowed_attach);
- if (ret < 0)
- goto done;
- data.n_attachments = n_attach;
- data.n_allowed_attachments = n_allowed_attach;
- if (copy_to_user(arg, &data, sizeof(data)))
- ret = -EFAULT;
- done:
- kfree(allowed_attach);
- kfree(attach);
- return ret;
- }
- static int adf_device_handle_attachment(struct adf_device *dev,
- struct adf_attachment_config __user *arg, bool attach)
- {
- struct adf_attachment_config data;
- struct adf_overlay_engine *eng;
- struct adf_interface *intf;
- if (copy_from_user(&data, arg, sizeof(data)))
- return -EFAULT;
- eng = idr_find(&dev->overlay_engines, data.overlay_engine);
- if (!eng) {
- dev_err(&dev->base.dev, "invalid overlay engine id %u\n",
- data.overlay_engine);
- return -EINVAL;
- }
- intf = idr_find(&dev->interfaces, data.interface);
- if (!intf) {
- dev_err(&dev->base.dev, "invalid interface id %u\n",
- data.interface);
- return -EINVAL;
- }
- if (attach)
- return adf_device_attach(dev, eng, intf);
- else
- return adf_device_detach(dev, eng, intf);
- }
- static int adf_intf_set_mode(struct adf_interface *intf,
- struct drm_mode_modeinfo __user *arg)
- {
- struct drm_mode_modeinfo mode;
- if (copy_from_user(&mode, arg, sizeof(mode)))
- return -EFAULT;
- return adf_interface_set_mode(intf, &mode);
- }
- static int adf_intf_get_data(struct adf_interface *intf,
- struct adf_interface_data __user *arg)
- {
- struct adf_device *dev = adf_interface_parent(intf);
- struct adf_interface_data data;
- struct drm_mode_modeinfo *modelist;
- size_t modelist_size;
- int err;
- int ret = 0;
- unsigned long flags;
- if (copy_from_user(&data, arg, sizeof(data)))
- return -EFAULT;
- strlcpy(data.name, intf->base.name, sizeof(data.name));
- data.type = intf->type;
- data.id = intf->idx;
- data.flags = intf->flags;
- err = adf_interface_get_screen_size(intf, &data.width_mm,
- &data.height_mm);
- if (err < 0) {
- data.width_mm = 0;
- data.height_mm = 0;
- }
- modelist = kmalloc(sizeof(modelist[0]) * ADF_MAX_MODES, GFP_KERNEL);
- if (!modelist)
- return -ENOMEM;
- mutex_lock(&dev->client_lock);
- read_lock_irqsave(&intf->hotplug_modelist_lock, flags);
- data.hotplug_detect = intf->hotplug_detect;
- modelist_size = min(data.n_available_modes, intf->n_modes) *
- sizeof(intf->modelist[0]);
- memcpy(modelist, intf->modelist, modelist_size);
- data.n_available_modes = intf->n_modes;
- read_unlock_irqrestore(&intf->hotplug_modelist_lock, flags);
- if (copy_to_user(arg->available_modes, modelist, modelist_size)) {
- ret = -EFAULT;
- goto done;
- }
- data.dpms_state = intf->dpms_state;
- memcpy(&data.current_mode, &intf->current_mode,
- sizeof(intf->current_mode));
- ret = adf_obj_copy_custom_data_to_user(&intf->base, arg->custom_data,
- &data.custom_data_size);
- done:
- mutex_unlock(&dev->client_lock);
- kfree(modelist);
- if (ret < 0)
- return ret;
- if (copy_to_user(arg, &data, sizeof(data)))
- ret = -EFAULT;
- return ret;
- }
- static inline long adf_obj_custom_ioctl(struct adf_obj *obj, unsigned int cmd,
- unsigned long arg)
- {
- if (obj->ops && obj->ops->ioctl)
- return obj->ops->ioctl(obj, cmd, arg);
- return -ENOTTY;
- }
- static long adf_overlay_engine_ioctl(struct adf_overlay_engine *eng,
- struct adf_file *file, unsigned int cmd, unsigned long arg)
- {
- switch (cmd) {
- case ADF_SET_EVENT:
- return adf_obj_set_event(&eng->base, file,
- (struct adf_set_event __user *)arg);
- case ADF_GET_OVERLAY_ENGINE_DATA:
- return adf_eng_get_data(eng,
- (struct adf_overlay_engine_data __user *)arg);
- case ADF_BLANK:
- case ADF_POST_CONFIG:
- case ADF_SET_MODE:
- case ADF_GET_DEVICE_DATA:
- case ADF_GET_INTERFACE_DATA:
- case ADF_SIMPLE_POST_CONFIG:
- case ADF_SIMPLE_BUFFER_ALLOC:
- case ADF_ATTACH:
- case ADF_DETACH:
- return -EINVAL;
- default:
- return adf_obj_custom_ioctl(&eng->base, cmd, arg);
- }
- }
- static long adf_interface_ioctl(struct adf_interface *intf,
- struct adf_file *file, unsigned int cmd, unsigned long arg)
- {
- switch (cmd) {
- case ADF_SET_EVENT:
- return adf_obj_set_event(&intf->base, file,
- (struct adf_set_event __user *)arg);
- case ADF_BLANK:
- return adf_interface_blank(intf, arg);
- case ADF_SET_MODE:
- return adf_intf_set_mode(intf,
- (struct drm_mode_modeinfo __user *)arg);
- case ADF_GET_INTERFACE_DATA:
- return adf_intf_get_data(intf,
- (struct adf_interface_data __user *)arg);
- case ADF_SIMPLE_POST_CONFIG:
- return adf_intf_simple_post_config(intf,
- (struct adf_simple_post_config __user *)arg);
- case ADF_SIMPLE_BUFFER_ALLOC:
- return adf_intf_simple_buffer_alloc(intf,
- (struct adf_simple_buffer_alloc __user *)arg);
- case ADF_POST_CONFIG:
- case ADF_GET_DEVICE_DATA:
- case ADF_GET_OVERLAY_ENGINE_DATA:
- case ADF_ATTACH:
- case ADF_DETACH:
- return -EINVAL;
- default:
- return adf_obj_custom_ioctl(&intf->base, cmd, arg);
- }
- }
- static long adf_device_ioctl(struct adf_device *dev, struct adf_file *file,
- unsigned int cmd, unsigned long arg)
- {
- switch (cmd) {
- case ADF_SET_EVENT:
- return adf_obj_set_event(&dev->base, file,
- (struct adf_set_event __user *)arg);
- case ADF_POST_CONFIG:
- return adf_device_post_config(dev,
- (struct adf_post_config __user *)arg);
- case ADF_GET_DEVICE_DATA:
- return adf_device_get_data(dev,
- (struct adf_device_data __user *)arg);
- case ADF_ATTACH:
- return adf_device_handle_attachment(dev,
- (struct adf_attachment_config __user *)arg,
- true);
- case ADF_DETACH:
- return adf_device_handle_attachment(dev,
- (struct adf_attachment_config __user *)arg,
- false);
- case ADF_BLANK:
- case ADF_SET_MODE:
- case ADF_GET_INTERFACE_DATA:
- case ADF_GET_OVERLAY_ENGINE_DATA:
- case ADF_SIMPLE_POST_CONFIG:
- case ADF_SIMPLE_BUFFER_ALLOC:
- return -EINVAL;
- default:
- return adf_obj_custom_ioctl(&dev->base, cmd, arg);
- }
- }
- static int adf_file_open(struct inode *inode, struct file *file)
- {
- struct adf_obj *obj;
- struct adf_file *fpriv = NULL;
- unsigned long flags;
- int ret = 0;
- obj = adf_obj_sysfs_find(iminor(inode));
- if (!obj)
- return -ENODEV;
- dev_dbg(&obj->dev, "opening %s\n", dev_name(&obj->dev));
- if (!try_module_get(obj->parent->ops->owner)) {
- dev_err(&obj->dev, "getting owner module failed\n");
- return -ENODEV;
- }
- fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
- if (!fpriv) {
- ret = -ENOMEM;
- goto done;
- }
- INIT_LIST_HEAD(&fpriv->head);
- fpriv->obj = obj;
- init_waitqueue_head(&fpriv->event_wait);
- file->private_data = fpriv;
- if (obj->ops && obj->ops->open) {
- ret = obj->ops->open(obj, inode, file);
- if (ret < 0)
- goto done;
- }
- spin_lock_irqsave(&obj->file_lock, flags);
- list_add_tail(&fpriv->head, &obj->file_list);
- spin_unlock_irqrestore(&obj->file_lock, flags);
- done:
- if (ret < 0) {
- kfree(fpriv);
- module_put(obj->parent->ops->owner);
- }
- return ret;
- }
- static int adf_file_release(struct inode *inode, struct file *file)
- {
- struct adf_file *fpriv = file->private_data;
- struct adf_obj *obj = fpriv->obj;
- enum adf_event_type event_type;
- unsigned long flags;
- if (obj->ops && obj->ops->release)
- obj->ops->release(obj, inode, file);
- spin_lock_irqsave(&obj->file_lock, flags);
- list_del(&fpriv->head);
- spin_unlock_irqrestore(&obj->file_lock, flags);
- for_each_set_bit(event_type, fpriv->event_subscriptions,
- ADF_EVENT_TYPE_MAX) {
- adf_event_put(obj, event_type);
- }
- kfree(fpriv);
- module_put(obj->parent->ops->owner);
- dev_dbg(&obj->dev, "released %s\n", dev_name(&obj->dev));
- return 0;
- }
- long adf_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
- {
- struct adf_file *fpriv = file->private_data;
- struct adf_obj *obj = fpriv->obj;
- long ret = -EINVAL;
- dev_dbg(&obj->dev, "%s ioctl %u\n", dev_name(&obj->dev), _IOC_NR(cmd));
- switch (obj->type) {
- case ADF_OBJ_OVERLAY_ENGINE:
- ret = adf_overlay_engine_ioctl(adf_obj_to_overlay_engine(obj),
- fpriv, cmd, arg);
- break;
- case ADF_OBJ_INTERFACE:
- ret = adf_interface_ioctl(adf_obj_to_interface(obj), fpriv, cmd,
- arg);
- break;
- case ADF_OBJ_DEVICE:
- ret = adf_device_ioctl(adf_obj_to_device(obj), fpriv, cmd, arg);
- break;
- }
- return ret;
- }
- static inline bool adf_file_event_available(struct adf_file *fpriv)
- {
- int head = fpriv->event_head;
- int tail = fpriv->event_tail;
- return CIRC_CNT(head, tail, sizeof(fpriv->event_buf)) != 0;
- }
- void adf_file_queue_event(struct adf_file *fpriv, struct adf_event *event)
- {
- int head = fpriv->event_head;
- int tail = fpriv->event_tail;
- size_t space = CIRC_SPACE(head, tail, sizeof(fpriv->event_buf));
- size_t space_to_end =
- CIRC_SPACE_TO_END(head, tail, sizeof(fpriv->event_buf));
- if (space < event->length) {
- dev_dbg(&fpriv->obj->dev,
- "insufficient buffer space for event %u\n",
- event->type);
- return;
- }
- if (space_to_end >= event->length) {
- memcpy(fpriv->event_buf + head, event, event->length);
- } else {
- memcpy(fpriv->event_buf + head, event, space_to_end);
- memcpy(fpriv->event_buf, (u8 *)event + space_to_end,
- event->length - space_to_end);
- }
- smp_wmb();
- fpriv->event_head = (fpriv->event_head + event->length) &
- (sizeof(fpriv->event_buf) - 1);
- wake_up_interruptible_all(&fpriv->event_wait);
- }
- static ssize_t adf_file_copy_to_user(struct adf_file *fpriv,
- char __user *buffer, size_t buffer_size)
- {
- int head, tail;
- u8 *event_buf;
- size_t cnt, cnt_to_end, copy_size = 0;
- ssize_t ret = 0;
- unsigned long flags;
- event_buf = kmalloc(min(buffer_size, sizeof(fpriv->event_buf)),
- GFP_KERNEL);
- if (!event_buf)
- return -ENOMEM;
- spin_lock_irqsave(&fpriv->obj->file_lock, flags);
- if (!adf_file_event_available(fpriv))
- goto out;
- head = fpriv->event_head;
- tail = fpriv->event_tail;
- cnt = CIRC_CNT(head, tail, sizeof(fpriv->event_buf));
- cnt_to_end = CIRC_CNT_TO_END(head, tail, sizeof(fpriv->event_buf));
- copy_size = min(buffer_size, cnt);
- if (cnt_to_end >= copy_size) {
- memcpy(event_buf, fpriv->event_buf + tail, copy_size);
- } else {
- memcpy(event_buf, fpriv->event_buf + tail, cnt_to_end);
- memcpy(event_buf + cnt_to_end, fpriv->event_buf,
- copy_size - cnt_to_end);
- }
- fpriv->event_tail = (fpriv->event_tail + copy_size) &
- (sizeof(fpriv->event_buf) - 1);
- out:
- spin_unlock_irqrestore(&fpriv->obj->file_lock, flags);
- if (copy_size) {
- if (copy_to_user(buffer, event_buf, copy_size))
- ret = -EFAULT;
- else
- ret = copy_size;
- }
- kfree(event_buf);
- return ret;
- }
- ssize_t adf_file_read(struct file *filp, char __user *buffer,
- size_t count, loff_t *offset)
- {
- struct adf_file *fpriv = filp->private_data;
- int err;
- err = wait_event_interruptible(fpriv->event_wait,
- adf_file_event_available(fpriv));
- if (err < 0)
- return err;
- return adf_file_copy_to_user(fpriv, buffer, count);
- }
- unsigned int adf_file_poll(struct file *filp, struct poll_table_struct *wait)
- {
- struct adf_file *fpriv = filp->private_data;
- unsigned int mask = 0;
- poll_wait(filp, &fpriv->event_wait, wait);
- if (adf_file_event_available(fpriv))
- mask |= POLLIN | POLLRDNORM;
- return mask;
- }
- const struct file_operations adf_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = adf_file_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = adf_file_compat_ioctl,
- #endif
- .open = adf_file_open,
- .release = adf_file_release,
- .llseek = default_llseek,
- .read = adf_file_read,
- .poll = adf_file_poll,
- };
|