drm: Manage struct drm_file with Linux list APIs
[dragonfly.git] / sys / dev / drm / drm_fops.c
1 /**
2  * \file drm_fops.c
3  * File operations for DRM
4  *
5  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6  * \author Daryll Strauss <daryll@valinux.com>
7  * \author Gareth Hughes <gareth@valinux.com>
8  */
9
10 /*
11  * Created: Mon Jan  4 08:58:31 1999 by faith@valinux.com
12  *
13  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
14  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
15  * All Rights Reserved.
16  *
17  * Permission is hereby granted, free of charge, to any person obtaining a
18  * copy of this software and associated documentation files (the "Software"),
19  * to deal in the Software without restriction, including without limitation
20  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
21  * and/or sell copies of the Software, and to permit persons to whom the
22  * Software is furnished to do so, subject to the following conditions:
23  *
24  * The above copyright notice and this permission notice (including the next
25  * paragraph) shall be included in all copies or substantial portions of the
26  * Software.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
29  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
30  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
31  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
32  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
33  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
34  * OTHER DEALINGS IN THE SOFTWARE.
35  *
36  * $FreeBSD: src/sys/dev/drm2/drm_fops.c,v 1.1 2012/05/22 11:07:44 kib Exp $
37  */
38
39 #include <sys/types.h>
40 #include <sys/conf.h>
41
42 #include <drm/drmP.h>
43
44 extern drm_pci_id_list_t *drm_find_description(int vendor, int device,
45     drm_pci_id_list_t *idlist);
46 extern devclass_t drm_devclass;
47
48 struct drm_file *drm_find_file_by_proc(struct drm_device *dev, DRM_STRUCTPROC *p)
49 {
50         uid_t uid = p->td_proc->p_ucred->cr_svuid;
51         pid_t pid = p->td_proc->p_pid;
52         struct drm_file *priv;
53
54         list_for_each_entry(priv, &dev->filelist, lhead)
55                 if (priv->pid == pid && priv->uid == uid)
56                         return priv;
57         return NULL;
58 }
59
60 static int drm_setup(struct drm_device *dev)
61 {
62         drm_local_map_t *map;
63         int i;
64
65         DRM_LOCK_ASSERT(dev);
66
67         /* prebuild the SAREA */
68         i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM,
69             _DRM_CONTAINS_LOCK, &map);
70         if (i != 0)
71                 return i;
72
73         if (dev->driver->firstopen)
74                 dev->driver->firstopen(dev);
75
76         dev->buf_use = 0;
77
78         if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) {
79                 i = drm_dma_setup(dev);
80                 if (i != 0)
81                         return i;
82         }
83
84         for (i = 0; i < DRM_HASH_SIZE; i++) {
85                 dev->magiclist[i].head = NULL;
86                 dev->magiclist[i].tail = NULL;
87         }
88
89         dev->lock.lock_queue = 0;
90         if (!drm_core_check_feature(dev, DRIVER_MODESET))
91                 dev->irq_enabled = 0;
92         dev->context_flag = 0;
93         dev->last_context = 0;
94         dev->if_version = 0;
95
96         dev->buf_sigio = NULL;
97
98         DRM_DEBUG("\n");
99
100         return 0;
101 }
102
103 #define DRIVER_SOFTC(unit) \
104         ((struct drm_device *)devclass_get_softc(drm_devclass, unit))
105
106 int
107 drm_open(struct dev_open_args *ap)
108 {
109         struct cdev *kdev = ap->a_head.a_dev;
110         int flags = ap->a_oflags;
111         int fmt = 0;
112         struct thread *p = curthread;
113         struct drm_device *dev;
114         int retcode;
115
116         dev = DRIVER_SOFTC(minor(kdev));
117         if (dev == NULL)
118                 return (ENXIO);
119
120         DRM_DEBUG("open_count = %d\n", dev->open_count);
121
122         retcode = drm_open_helper(kdev, flags, fmt, p, dev);
123
124         if (retcode == 0) {
125                 atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
126                 DRM_LOCK(dev);
127                 device_busy(dev->dev);
128                 if (!dev->open_count++)
129                         retcode = drm_setup(dev);
130                 DRM_UNLOCK(dev);
131         }
132
133         DRM_DEBUG("return %d\n", retcode);
134
135         return (retcode);
136 }
137
138 /* drm_open_helper is called whenever a process opens /dev/drm. */
139 int drm_open_helper(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p,
140                     struct drm_device *dev)
141 {
142         struct drm_file *priv;
143         int retcode;
144
145         if (flags & O_EXCL)
146                 return EBUSY; /* No exclusive opens */
147         dev->flags = flags;
148
149         DRM_DEBUG("pid = %d, device = %s\n", DRM_CURRENTPID, devtoname(kdev));
150         DRM_LOCK(dev);
151
152         priv = drm_find_file_by_proc(dev, p);
153         if (priv) {
154                 goto priv_found;
155         }
156
157         priv = kmalloc(sizeof(*priv), DRM_MEM_FILES, M_NOWAIT | M_ZERO);
158         if (priv == NULL) {
159                 return ENOMEM;
160         }
161         
162 priv_found:
163
164         priv->dev               = dev;
165         priv->uid               = p->td_proc->p_ucred->cr_svuid;
166         priv->pid               = p->td_proc->p_pid;
167         priv->ioctl_count       = 0;
168
169         /* for compatibility root is always authenticated */
170         priv->authenticated     = DRM_SUSER(p);
171
172         INIT_LIST_HEAD(&priv->fbs);
173         INIT_LIST_HEAD(&priv->event_list);
174         priv->event_space = 4096; /* set aside 4k for event buffer */
175
176         if (dev->driver->driver_features & DRIVER_GEM)
177                 drm_gem_open(dev, priv);
178
179         if (dev->driver->open) {
180                 /* shared code returns -errno */
181                 retcode = -dev->driver->open(dev, priv);
182                 if (retcode != 0) {
183                         drm_free(priv, DRM_MEM_FILES);
184                         DRM_UNLOCK(dev);
185                         return retcode;
186                 }
187         }
188
189         /* first opener automatically becomes master */
190         priv->master = list_empty(&dev->filelist);
191
192         list_add(&priv->lhead, &dev->filelist);
193         DRM_UNLOCK(dev);
194         kdev->si_drv1 = dev;
195         return 0;
196 }
197
198 static bool
199 drm_dequeue_event(struct drm_device *dev, struct drm_file *file_priv,
200     struct uio *uio, struct drm_pending_event **out)
201 {
202         struct drm_pending_event *e;
203
204         if (list_empty(&file_priv->event_list))
205                 return (false);
206         e = list_first_entry(&file_priv->event_list,
207             struct drm_pending_event, link);
208         if (e->event->length > uio->uio_resid)
209                 return (false);
210
211         file_priv->event_space += e->event->length;
212         list_del(&e->link);
213         *out = e;
214         return (true);
215 }
216
217 int
218 drm_read(struct dev_read_args *ap)
219 {
220         struct cdev *kdev = ap->a_head.a_dev;
221         struct uio *uio = ap->a_uio;
222         int ioflag = ap->a_ioflag;
223         struct drm_file *file_priv;
224         struct drm_device *dev;
225         struct drm_pending_event *e;
226         int error;
227
228         dev = drm_get_device_from_kdev(kdev);
229         file_priv = drm_find_file_by_proc(dev, curthread);
230
231         lockmgr(&dev->event_lock, LK_EXCLUSIVE);
232         while (list_empty(&file_priv->event_list)) {
233                 if ((ioflag & O_NONBLOCK) != 0) {
234                         error = EAGAIN;
235                         goto out;
236                 }
237                 error = lksleep(&file_priv->event_space, &dev->event_lock,
238                    PCATCH, "drmrea", 0);
239                if (error != 0)
240                        goto out;
241         }
242         while (drm_dequeue_event(dev, file_priv, uio, &e)) {
243                 lockmgr(&dev->event_lock, LK_RELEASE);
244                 error = uiomove((caddr_t)e->event, e->event->length, uio);
245                 e->destroy(e);
246                 if (error != 0)
247                         return (error);
248                 lockmgr(&dev->event_lock, LK_EXCLUSIVE);
249         }
250 out:
251         lockmgr(&dev->event_lock, LK_RELEASE);
252         return (error);
253 }
254
255 void
256 drm_event_wakeup(struct drm_pending_event *e)
257 {
258         struct drm_file *file_priv;
259         struct drm_device *dev;
260
261         file_priv = e->file_priv;
262         dev = file_priv->dev;
263         KKASSERT(lockstatus(&dev->event_lock, curthread) != 0);
264
265         wakeup(&file_priv->event_space);
266 }
267
268 static int
269 drmfilt(struct knote *kn, long hint)
270 {
271         return (0);
272 }
273
274 static void
275 drmfilt_detach(struct knote *kn) {}
276
277 static struct filterops drmfiltops =
278         { FILTEROP_ISFD, NULL, drmfilt_detach, drmfilt };
279
280 int
281 drm_kqfilter(struct dev_kqfilter_args *ap)
282 {
283         struct knote *kn = ap->a_kn;
284
285         ap->a_result = 0;
286
287         switch (kn->kn_filter) {
288         case EVFILT_READ:
289         case EVFILT_WRITE:
290                 kn->kn_fop = &drmfiltops;
291                 break;
292         default:
293                 ap->a_result = EOPNOTSUPP;
294                 return (0);
295         }
296
297         return (0);
298 }