#include <sys/types.h>
#include <sys/uio.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <drm/drm_client.h>
#include <drm/drm_file.h>
#include <drm/drmP.h>
#include "drm_legacy.h"
#include "drm_internal.h"
#include "drm_crtc_internal.h"
static void drm_events_release(struct drm_file *file_priv);
DEFINE_MUTEX(drm_global_mutex);
static int drm_open_helper(struct cdev *kdev, int flags,
struct file *filp, struct drm_minor *minor);
struct drm_file *drm_file_alloc(struct drm_minor *minor)
{
struct drm_device *dev = minor->dev;
struct drm_file *file;
int ret;
file = kzalloc(sizeof(*file), GFP_KERNEL);
if (!file)
return ERR_PTR(-ENOMEM);
#ifdef __DragonFly__
file->pid = curproc->p_pid;
#else
file->pid = get_pid(task_pid(current));
#endif
file->minor = minor;
file->authenticated = capable(CAP_SYS_ADMIN);
file->lock_count = 0;
INIT_LIST_HEAD(&file->lhead);
INIT_LIST_HEAD(&file->fbs);
lockinit(&file->fbs_lock, "dpfl", 0, LK_CANRECURSE);
INIT_LIST_HEAD(&file->blobs);
INIT_LIST_HEAD(&file->pending_event_list);
INIT_LIST_HEAD(&file->event_list);
init_waitqueue_head(&file->event_wait);
file->event_space = 4096;
lockinit(&file->event_read_lock, "dperl", 0, LK_CANRECURSE);
if (drm_core_check_feature(dev, DRIVER_GEM))
drm_gem_open(dev, file);
if (drm_core_check_feature(dev, DRIVER_SYNCOBJ))
drm_syncobj_open(file);
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_prime_init_file_private(&file->prime);
if (dev->driver->open) {
ret = dev->driver->open(dev, file);
if (ret < 0)
goto out_prime_destroy;
}
return file;
out_prime_destroy:
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_prime_destroy_file_private(&file->prime);
if (drm_core_check_feature(dev, DRIVER_SYNCOBJ))
drm_syncobj_release(file);
if (drm_core_check_feature(dev, DRIVER_GEM))
drm_gem_release(dev, file);
put_pid(file->pid);
kfree(file);
return ERR_PTR(ret);
}
static void drm_events_release(struct drm_file *file_priv)
{
struct drm_device *dev = file_priv->minor->dev;
struct drm_pending_event *e, *et;
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
list_for_each_entry_safe(e, et, &file_priv->pending_event_list,
pending_link) {
list_del(&e->pending_link);
e->file_priv = NULL;
}
list_for_each_entry_safe(e, et, &file_priv->event_list, link) {
list_del(&e->link);
kfree(e);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
}
void drm_file_free(struct drm_file *file)
{
struct drm_device *dev;
if (!file)
return;
dev = file->minor->dev;
DRM_DEBUG("pid = %d, device = 0x%p, open_count = %d\n",
curproc->p_pid,
dev,
dev->open_count);
if (drm_core_check_feature(dev, DRIVER_LEGACY) &&
dev->driver->preclose)
dev->driver->preclose(dev, file);
if (drm_core_check_feature(dev, DRIVER_LEGACY))
drm_legacy_lock_release(dev, file->filp);
if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
drm_legacy_reclaim_buffers(dev, file);
drm_events_release(file);
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
drm_fb_release(file);
drm_property_destroy_user_blobs(dev, file);
}
if (drm_core_check_feature(dev, DRIVER_SYNCOBJ))
drm_syncobj_release(file);
if (drm_core_check_feature(dev, DRIVER_GEM))
drm_gem_release(dev, file);
drm_legacy_ctxbitmap_flush(dev, file);
if (drm_is_primary_client(file))
drm_master_release(file);
if (dev->driver->postclose)
dev->driver->postclose(dev, file);
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_prime_destroy_file_private(&file->prime);
WARN_ON(!list_empty(&file->event_list));
put_pid(file->pid);
kfree(file);
}
static int drm_setup(struct drm_device * dev)
{
int ret;
if (dev->driver->firstopen &&
drm_core_check_feature(dev, DRIVER_LEGACY)) {
ret = dev->driver->firstopen(dev);
if (ret != 0)
return ret;
}
ret = drm_legacy_dma_setup(dev);
if (ret < 0)
return ret;
DRM_DEBUG("\n");
return 0;
}
#if 0
int drm_open(struct inode *inode, struct file *filp)
#endif
int drm_open(struct dev_open_args *ap)
{
#ifdef __DragonFly__
struct file *filp = ap->a_fpp ? *ap->a_fpp : NULL;
struct inode *inode = filp->f_data;
struct cdev *kdev = ap->a_head.a_dev;
int flags = ap->a_oflags;
#endif
struct drm_device *dev;
struct drm_minor *minor;
int retcode;
int need_setup = 0;
minor = drm_minor_acquire(iminor(inode));
if (IS_ERR(minor))
return PTR_ERR(minor);
dev = minor->dev;
if (!dev->open_count++)
need_setup = 1;
#if 0
filp->f_mapping = dev->anon_inode->i_mapping;
#endif
#ifdef __DragonFly__
retcode = drm_open_helper(kdev, flags, filp, minor);
#else
retcode = drm_open_helper(filp, minor);
#endif
if (retcode)
goto err_undo;
if (need_setup) {
retcode = drm_setup(dev);
if (retcode)
goto err_undo;
}
#ifdef __DragonFly__
device_busy(dev->dev->bsddev);
#endif
return 0;
err_undo:
dev->open_count--;
drm_minor_release(minor);
return retcode;
}
EXPORT_SYMBOL(drm_open);
int
drm_close(struct dev_close_args *ap)
{
#ifdef __DragonFly__
struct file *filp = ap->a_fp;
struct inode *inode = filp->f_data;
#endif
struct drm_file *file_priv = filp->private_data;
struct drm_minor *minor = drm_minor_acquire(iminor(inode));
struct drm_device *dev = minor->dev;
mutex_lock(&drm_global_mutex);
DRM_DEBUG("open_count = %d\n", dev->open_count);
mutex_lock(&dev->filelist_mutex);
list_del(&file_priv->lhead);
mutex_unlock(&dev->filelist_mutex);
if (drm_core_check_feature(dev, DRIVER_LEGACY) &&
dev->driver->preclose)
dev->driver->preclose(dev, file_priv);
DRM_DEBUG("pid = %d, device = 0x%p, open_count = %d\n",
curproc->p_pid,
dev,
dev->open_count);
if (drm_core_check_feature(dev, DRIVER_LEGACY))
drm_legacy_lock_release(dev, filp);
if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
drm_legacy_reclaim_buffers(dev, file_priv);
drm_events_release(file_priv);
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
drm_fb_release(file_priv);
drm_property_destroy_user_blobs(dev, file_priv);
}
if (drm_core_check_feature(dev, DRIVER_SYNCOBJ))
drm_syncobj_release(file_priv);
if (drm_core_check_feature(dev, DRIVER_GEM))
drm_gem_release(dev, file_priv);
drm_legacy_ctxbitmap_flush(dev, file_priv);
if (drm_is_primary_client(file_priv))
drm_master_release(file_priv);
if (dev->driver->postclose)
dev->driver->postclose(dev, file_priv);
if (drm_core_check_feature(dev, DRIVER_PRIME))
drm_prime_destroy_file_private(&file_priv->prime);
WARN_ON(!list_empty(&file_priv->event_list));
put_pid(file_priv->pid);
kfree(file_priv);
if (!--dev->open_count) {
drm_lastclose(dev);
#if 0
if (drm_dev_is_unplugged(dev))
drm_put_dev(dev);
#endif
}
mutex_unlock(&drm_global_mutex);
drm_minor_release(minor);
return 0;
}
EXPORT_SYMBOL(drm_close);
static int drm_cpu_valid(void)
{
#if defined(__sparc__) && !defined(__sparc_v9__)
return 0;
#endif
return 1;
}
static int drm_open_helper(struct cdev *kdev, int flags,
struct file *filp, struct drm_minor *minor)
{
struct drm_device *dev = minor->dev;
struct drm_file *priv;
int ret;
if (flags & O_EXCL)
return -EBUSY;
if (!drm_cpu_valid())
return -EINVAL;
#if 0
if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF)
return -EINVAL;
#endif
DRM_DEBUG("pid = %d, minor = %d\n", DRM_CURRENTPID, minor->index);
priv = drm_file_alloc(minor);
if (IS_ERR(priv))
return PTR_ERR(priv);
#ifdef __DragonFly__
kdev->si_drv1 = dev;
#endif
if (drm_is_primary_client(priv)) {
ret = drm_master_open(priv);
if (ret) {
drm_file_free(priv);
return ret;
}
}
filp->private_data = priv;
#if 0
filp->f_mode |= FMODE_UNSIGNED_OFFSET;
#endif
priv->filp = filp;
mutex_lock(&dev->filelist_mutex);
list_add(&priv->lhead, &dev->filelist);
mutex_unlock(&dev->filelist_mutex);
#ifdef __alpha__
if (!dev->hose) {
struct pci_dev *pci_dev;
pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL);
if (pci_dev) {
dev->hose = pci_dev->sysdata;
pci_dev_put(pci_dev);
}
if (!dev->hose) {
struct pci_bus *b = list_entry(pci_root_buses.next,
struct pci_bus, node);
if (b)
dev->hose = b->sysdata;
}
}
#endif
return 0;
}
static void drm_legacy_dev_reinit(struct drm_device *dev)
{
if (dev->irq_enabled)
drm_irq_uninstall(dev);
mutex_lock(&dev->struct_mutex);
drm_legacy_agp_clear(dev);
drm_legacy_sg_cleanup(dev);
drm_legacy_vma_flush(dev);
drm_legacy_dma_takedown(dev);
mutex_unlock(&dev->struct_mutex);
dev->sigdata.lock = NULL;
dev->context_flag = 0;
dev->last_context = 0;
dev->if_version = 0;
DRM_DEBUG("lastclose completed\n");
}
void drm_lastclose(struct drm_device * dev)
{
DRM_DEBUG("\n");
if (dev->driver->lastclose)
dev->driver->lastclose(dev);
DRM_DEBUG("driver lastclose completed\n");
if (drm_core_check_feature(dev, DRIVER_LEGACY))
drm_legacy_dev_reinit(dev);
#if 0
drm_client_dev_restore(dev);
#endif
}
int drm_release(struct inode *inode, struct file *filp)
{
struct drm_file *file_priv = filp->private_data;
struct drm_minor *minor = file_priv->minor;
struct drm_device *dev = minor->dev;
#ifdef __DragonFly__
return 0;
#endif
mutex_lock(&drm_global_mutex);
DRM_DEBUG("open_count = %d\n", dev->open_count);
mutex_lock(&dev->filelist_mutex);
list_del(&file_priv->lhead);
mutex_unlock(&dev->filelist_mutex);
drm_file_free(file_priv);
if (!--dev->open_count) {
drm_lastclose(dev);
if (drm_dev_is_unplugged(dev))
drm_put_dev(dev);
}
mutex_unlock(&drm_global_mutex);
drm_minor_release(minor);
return 0;
}
EXPORT_SYMBOL(drm_release);
int drm_read(struct dev_read_args *ap)
{
struct file *filp = ap->a_fp;
struct uio *uio = ap->a_uio;
size_t count = uio->uio_resid;
struct drm_file *file_priv = filp->private_data;
struct drm_device *dev = file_priv->minor->dev;
int ret = 0;
ret = mutex_lock_interruptible(&file_priv->event_read_lock);
if (ret)
return ret;
for (;;) {
struct drm_pending_event *e = NULL;
spin_lock_irq(&dev->event_lock);
if (!list_empty(&file_priv->event_list)) {
e = list_first_entry(&file_priv->event_list,
struct drm_pending_event, link);
file_priv->event_space += e->event->length;
list_del(&e->link);
}
spin_unlock_irq(&dev->event_lock);
if (e == NULL) {
if (ret) {
ret = 0;
break;
}
if (filp->f_flag & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
mutex_unlock(&file_priv->event_read_lock);
ret = wait_event_interruptible(file_priv->event_wait,
!list_empty(&file_priv->event_list));
if (ret >= 0)
ret = mutex_lock_interruptible(&file_priv->event_read_lock);
if (ret)
return ret;
} else {
unsigned length = e->event->length;
if (length > count - ret) {
put_back_event:
spin_lock_irq(&dev->event_lock);
file_priv->event_space -= length;
list_add(&e->link, &file_priv->event_list);
spin_unlock_irq(&dev->event_lock);
break;
}
if (uiomove((caddr_t)e->event, length, uio)) {
if (ret == 0)
ret = -EFAULT;
goto put_back_event;
}
ret += length;
kfree(e);
}
}
mutex_unlock(&file_priv->event_read_lock);
return ret;
}
EXPORT_SYMBOL(drm_read);
static int
drmfilt(struct knote *kn, long hint)
{
struct drm_file *file_priv = (struct drm_file *)kn->kn_hook;
int ready = 0;
if (!list_empty(&file_priv->event_list))
ready = 1;
return (ready);
}
static void
drmfilt_detach(struct knote *kn)
{
struct drm_file *file_priv;
struct klist *klist;
file_priv = (struct drm_file *)kn->kn_hook;
klist = &file_priv->dkq.ki_note;
knote_remove(klist, kn);
}
static struct filterops drmfiltops =
{ FILTEROP_MPSAFE | FILTEROP_ISFD, NULL, drmfilt_detach, drmfilt };
int
drm_kqfilter(struct dev_kqfilter_args *ap)
{
struct file *filp = ap->a_fp;
struct drm_file *file_priv = filp->private_data;
struct knote *kn = ap->a_kn;
struct klist *klist;
ap->a_result = 0;
switch (kn->kn_filter) {
case EVFILT_READ:
case EVFILT_WRITE:
kn->kn_fop = &drmfiltops;
kn->kn_hook = (caddr_t)file_priv;
break;
default:
ap->a_result = EOPNOTSUPP;
return (0);
}
klist = &file_priv->dkq.ki_note;
knote_insert(klist, kn);
return (0);
}
int drm_event_reserve_init_locked(struct drm_device *dev,
struct drm_file *file_priv,
struct drm_pending_event *p,
struct drm_event *e)
{
if (file_priv->event_space < e->length)
return -ENOMEM;
file_priv->event_space -= e->length;
p->event = e;
list_add(&p->pending_link, &file_priv->pending_event_list);
p->file_priv = file_priv;
return 0;
}
EXPORT_SYMBOL(drm_event_reserve_init_locked);
int drm_event_reserve_init(struct drm_device *dev,
struct drm_file *file_priv,
struct drm_pending_event *p,
struct drm_event *e)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&dev->event_lock, flags);
ret = drm_event_reserve_init_locked(dev, file_priv, p, e);
spin_unlock_irqrestore(&dev->event_lock, flags);
return ret;
}
EXPORT_SYMBOL(drm_event_reserve_init);
void drm_event_cancel_free(struct drm_device *dev,
struct drm_pending_event *p)
{
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
if (p->file_priv) {
p->file_priv->event_space += p->event->length;
list_del(&p->pending_link);
}
spin_unlock_irqrestore(&dev->event_lock, flags);
if (p->fence)
dma_fence_put(p->fence);
kfree(p);
}
EXPORT_SYMBOL(drm_event_cancel_free);
void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e)
{
assert_spin_locked(&dev->event_lock);
if (e->completion) {
complete_all(e->completion);
e->completion_release(e->completion);
e->completion = NULL;
}
if (e->fence) {
dma_fence_signal(e->fence);
dma_fence_put(e->fence);
}
if (!e->file_priv) {
kfree(e);
return;
}
list_del(&e->pending_link);
list_add_tail(&e->link,
&e->file_priv->event_list);
wake_up_interruptible(&e->file_priv->event_wait);
#ifdef __DragonFly__
KNOTE(&e->file_priv->dkq.ki_note, 0);
#endif
}
EXPORT_SYMBOL(drm_send_event_locked);
void drm_send_event(struct drm_device *dev, struct drm_pending_event *e)
{
unsigned long irqflags;
spin_lock_irqsave(&dev->event_lock, irqflags);
drm_send_event_locked(dev, e);
spin_unlock_irqrestore(&dev->event_lock, irqflags);
}
EXPORT_SYMBOL(drm_send_event);