Merge branch 'vendor/LIBPCAP'
[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 #include <sys/devfs.h>
42
43 #include <drm/drmP.h>
44
45 extern drm_pci_id_list_t *drm_find_description(int vendor, int device,
46     drm_pci_id_list_t *idlist);
47 extern devclass_t drm_devclass;
48
49 static int drm_setup(struct drm_device *dev)
50 {
51         drm_local_map_t *map;
52         int i;
53
54         DRM_LOCK_ASSERT(dev);
55
56         /* prebuild the SAREA */
57         i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM,
58             _DRM_CONTAINS_LOCK, &map);
59         if (i != 0)
60                 return i;
61
62         if (dev->driver->firstopen)
63                 dev->driver->firstopen(dev);
64
65         dev->buf_use = 0;
66
67         if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) {
68                 i = drm_dma_setup(dev);
69                 if (i != 0)
70                         return i;
71         }
72
73         for (i = 0; i < DRM_HASH_SIZE; i++) {
74                 dev->magiclist[i].head = NULL;
75                 dev->magiclist[i].tail = NULL;
76         }
77
78         dev->lock.lock_queue = 0;
79         if (!drm_core_check_feature(dev, DRIVER_MODESET))
80                 dev->irq_enabled = 0;
81         dev->context_flag = 0;
82         dev->last_context = 0;
83         dev->if_version = 0;
84
85         dev->buf_sigio = NULL;
86
87         DRM_DEBUG("\n");
88
89         return 0;
90 }
91
92 #define DRIVER_SOFTC(unit) \
93         ((struct drm_device *)devclass_get_softc(drm_devclass, unit))
94
95 int
96 drm_open(struct dev_open_args *ap)
97 {
98         struct cdev *kdev = ap->a_head.a_dev;
99         int flags = ap->a_oflags;
100         int fmt = 0;
101         struct thread *p = curthread;
102         struct drm_device *dev;
103         int retcode;
104
105         dev = DRIVER_SOFTC(minor(kdev));
106         if (dev == NULL)
107                 return (ENXIO);
108
109         DRM_DEBUG("open_count = %d\n", dev->open_count);
110
111         retcode = drm_open_helper(kdev, flags, fmt, p, dev, ap->a_fp);
112
113         if (retcode == 0) {
114                 atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
115                 DRM_LOCK(dev);
116                 device_busy(dev->dev);
117                 if (!dev->open_count++)
118                         retcode = drm_setup(dev);
119                 DRM_UNLOCK(dev);
120         }
121
122         DRM_DEBUG("return %d\n", retcode);
123
124         return (retcode);
125 }
126
127 /* drm_open_helper is called whenever a process opens /dev/drm. */
128 int drm_open_helper(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p,
129                     struct drm_device *dev, struct file *fp)
130 {
131         struct drm_file *priv;
132         int retcode;
133
134         if (flags & O_EXCL)
135                 return EBUSY; /* No exclusive opens */
136         dev->flags = flags;
137
138         DRM_DEBUG("pid = %d, device = %s\n", DRM_CURRENTPID, devtoname(kdev));
139
140         priv = kmalloc(sizeof(*priv), DRM_MEM_FILES, M_NOWAIT | M_ZERO);
141         if (priv == NULL) {
142                 return ENOMEM;
143         }
144         
145         DRM_LOCK(dev);
146         priv->dev               = dev;
147         priv->uid               = p->td_proc->p_ucred->cr_svuid;
148         priv->pid               = p->td_proc->p_pid;
149         priv->ioctl_count       = 0;
150
151         /* for compatibility root is always authenticated */
152         priv->authenticated     = DRM_SUSER(p);
153
154         INIT_LIST_HEAD(&priv->fbs);
155         INIT_LIST_HEAD(&priv->event_list);
156         init_waitqueue_head(&priv->event_wait);
157         priv->event_space = 4096; /* set aside 4k for event buffer */
158
159         if (dev->driver->driver_features & DRIVER_GEM)
160                 drm_gem_open(dev, priv);
161
162         if (dev->driver->open) {
163                 /* shared code returns -errno */
164                 retcode = -dev->driver->open(dev, priv);
165                 if (retcode != 0) {
166                         drm_free(priv, DRM_MEM_FILES);
167                         DRM_UNLOCK(dev);
168                         return retcode;
169                 }
170         }
171
172         /* first opener automatically becomes master */
173         priv->master = list_empty(&dev->filelist);
174
175         list_add(&priv->lhead, &dev->filelist);
176         DRM_UNLOCK(dev);
177         kdev->si_drv1 = dev;
178
179         retcode = devfs_set_cdevpriv(fp, priv, &drm_cdevpriv_dtor);
180         if (retcode != 0)
181                 drm_cdevpriv_dtor(priv);
182
183         return retcode;
184 }
185
186 static bool
187 drm_dequeue_event(struct drm_device *dev, struct drm_file *file_priv,
188     struct uio *uio, struct drm_pending_event **out)
189 {
190         struct drm_pending_event *e;
191
192         if (list_empty(&file_priv->event_list))
193                 return (false);
194         e = list_first_entry(&file_priv->event_list,
195             struct drm_pending_event, link);
196         if (e->event->length > uio->uio_resid)
197                 return (false);
198
199         file_priv->event_space += e->event->length;
200         list_del(&e->link);
201         *out = e;
202         return (true);
203 }
204
205 int
206 drm_read(struct dev_read_args *ap)
207 {
208         struct cdev *kdev = ap->a_head.a_dev;
209         struct uio *uio = ap->a_uio;
210         int ioflag = ap->a_ioflag;
211         struct drm_file *file_priv;
212         struct drm_device *dev;
213         struct drm_pending_event *e;
214         int error;
215
216         error = devfs_get_cdevpriv(ap->a_fp, (void **)&file_priv);
217         if (error != 0) {
218                 DRM_ERROR("can't find authenticator\n");
219                 return (EINVAL);
220         }
221         dev = drm_get_device_from_kdev(kdev);
222         lockmgr(&dev->event_lock, LK_EXCLUSIVE);
223         while (list_empty(&file_priv->event_list)) {
224                 if ((ioflag & O_NONBLOCK) != 0) {
225                         error = EAGAIN;
226                         goto out;
227                 }
228                 error = lksleep(&file_priv->event_space, &dev->event_lock,
229                    PCATCH, "drmrea", 0);
230                if (error != 0)
231                        goto out;
232         }
233         while (drm_dequeue_event(dev, file_priv, uio, &e)) {
234                 lockmgr(&dev->event_lock, LK_RELEASE);
235                 error = uiomove((caddr_t)e->event, e->event->length, uio);
236                 e->destroy(e);
237                 if (error != 0)
238                         return (error);
239                 lockmgr(&dev->event_lock, LK_EXCLUSIVE);
240         }
241 out:
242         lockmgr(&dev->event_lock, LK_RELEASE);
243         return (error);
244 }
245
246 void
247 drm_event_wakeup(struct drm_pending_event *e)
248 {
249         struct drm_file *file_priv;
250         struct drm_device *dev;
251
252         file_priv = e->file_priv;
253         dev = file_priv->dev;
254         KKASSERT(lockstatus(&dev->event_lock, curthread) != 0);
255
256         wakeup(&file_priv->event_space);
257 }
258
259 static int
260 drmfilt(struct knote *kn, long hint)
261 {
262         return (0);
263 }
264
265 static void
266 drmfilt_detach(struct knote *kn) {}
267
268 static struct filterops drmfiltops =
269         { FILTEROP_ISFD, NULL, drmfilt_detach, drmfilt };
270
271 int
272 drm_kqfilter(struct dev_kqfilter_args *ap)
273 {
274         struct knote *kn = ap->a_kn;
275
276         ap->a_result = 0;
277
278         switch (kn->kn_filter) {
279         case EVFILT_READ:
280         case EVFILT_WRITE:
281                 kn->kn_fop = &drmfiltops;
282                 break;
283         default:
284                 ap->a_result = EOPNOTSUPP;
285                 return (0);
286         }
287
288         return (0);
289 }