/* * 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, };