1/* $NetBSD: drm_fops.c,v 1.4 2014/07/16 20:56:25 riastradh Exp $ */
2
3/*-
4 * Copyright (c) 2013 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Taylor R. Campbell.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: drm_fops.c,v 1.4 2014/07/16 20:56:25 riastradh Exp $");
34
35#include <sys/param.h>
36#include <sys/select.h>
37
38#include <drm/drmP.h>
39
40static int drm_open_file_master(struct drm_file *);
41
42static void drm_master_release(struct drm_file *);
43static void drm_events_release(struct drm_file *);
44static void drm_close_file_contexts(struct drm_file *);
45static void drm_close_file_master(struct drm_file *);
46
47int
48drm_open_file(struct drm_file *file, void *fp, struct drm_minor *minor)
49{
50 struct drm_device *const dev = minor->dev;
51 int ret;
52
53 file->always_authenticated = DRM_SUSER(); /* XXX */
54 file->always_authenticated = file->always_authenticated;
55 file->is_master = false;
56 file->stereo_allowed = false;
57 file->universal_planes = false;
58 file->magic = 0;
59 INIT_LIST_HEAD(&file->lhead);
60 file->minor = minor;
61 file->lock_count = 0;
62 /* file->object_idr is initialized by drm_gem_open. */
63 /* file->table_lock is initialized by drm_gem_open. */
64 file->filp = fp;
65 file->driver_priv = NULL;
66 file->master = NULL;
67 INIT_LIST_HEAD(&file->fbs);
68 linux_mutex_init(&file->fbs_lock);
69 DRM_INIT_WAITQUEUE(&file->event_wait, "drmevent");
70 selinit(&file->event_selq);
71 INIT_LIST_HEAD(&file->event_list);
72 file->event_space = 0x1000; /* XXX cargo-culted from Linux */
73
74 if (drm_core_check_feature(dev, DRIVER_GEM))
75 drm_gem_open(dev, file);
76#ifndef __NetBSD__ /* XXX drm prime */
77 if (drm_core_check_feature(dev, DRIVER_PRIME))
78 drm_prime_init_file_private(&file->prime);
79#endif
80
81 if (dev->driver->open) {
82 ret = (*dev->driver->open)(dev, file);
83 if (ret)
84 goto fail0;
85 }
86
87 ret = drm_open_file_master(file);
88 if (ret)
89 goto fail1;
90
91 mutex_lock(&dev->struct_mutex);
92 list_add(&file->lhead, &dev->filelist);
93 mutex_unlock(&dev->struct_mutex);
94
95 /* Success! */
96 return 0;
97
98fail1: /*
99 * XXX This error branch needs scrutiny, but Linux's error
100 * branches are incomprehensible and look wronger.
101 */
102 if (dev->driver->preclose)
103 (*dev->driver->preclose)(dev, file);
104 if (dev->driver->postclose)
105 (*dev->driver->postclose)(dev, file);
106fail0:
107#ifndef __NetBSD__ /* XXX drm prime */
108 if (drm_core_check_feature(dev, DRIVER_PRIME))
109 drm_prime_destroy_file_private(&file->prime);
110#endif
111 if (drm_core_check_feature(dev, DRIVER_GEM))
112 drm_gem_release(dev, file);
113 return ret;
114}
115
116static int
117drm_open_file_master(struct drm_file *file)
118{
119 struct drm_device *const dev = file->minor->dev;
120 int ret;
121
122 mutex_lock(&dev->master_mutex);
123 if (file->minor->master != NULL) {
124 file->master = drm_master_get(file->minor->master);
125 } else {
126 file->minor->master = drm_master_create(file->minor);
127 if (file->minor->master == NULL) {
128 ret = -ENOMEM;
129 goto fail0;
130 }
131
132 file->is_master = 1;
133 file->master = drm_master_get(file->minor->master);
134 file->authenticated = 1;
135
136 if (dev->driver->master_create) {
137 ret = (*dev->driver->master_create)(dev,
138 file->minor->master);
139 if (ret)
140 goto fail1;
141 }
142
143 if (dev->driver->master_set) {
144 ret = (*dev->driver->master_set)(dev, file, true);
145 if (ret)
146 goto fail1;
147 }
148 }
149 mutex_unlock(&dev->master_mutex);
150
151 /* Success! */
152 return 0;
153
154fail1: mutex_unlock(&dev->master_mutex);
155 /* drm_master_put handles calling master_destroy for us. */
156 drm_master_put(&file->minor->master);
157 drm_master_put(&file->master);
158fail0: KASSERT(ret);
159 return ret;
160}
161
162void
163drm_close_file(struct drm_file *file)
164{
165 struct drm_minor *const minor = file->minor;
166 struct drm_device *const dev = minor->dev;
167
168 if (dev->driver->preclose)
169 (*dev->driver->preclose)(dev, file);
170
171 if (file->magic)
172 (void)drm_remove_magic(file->master, file->magic);
173 if (minor->master)
174 drm_master_release(file);
175 if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
176 drm_core_reclaim_buffers(dev, file);
177 drm_events_release(file);
178 if (drm_core_check_feature(dev, DRIVER_MODESET))
179 drm_fb_release(file);
180 if (drm_core_check_feature(dev, DRIVER_GEM))
181 drm_gem_release(dev, file);
182 drm_close_file_contexts(file);
183 drm_close_file_master(file);
184
185 mutex_lock(&dev->struct_mutex);
186 list_del(&file->lhead);
187 mutex_unlock(&dev->struct_mutex);
188
189 if (dev->driver->postclose)
190 (*dev->driver->postclose)(dev, file);
191#ifndef __NetBSD__ /* XXX drm prime */
192 if (drm_core_check_feature(dev, DRIVER_PRIME))
193 drm_prime_destroy_file_private(&file->prime);
194#endif
195
196
197 seldestroy(&file->event_selq);
198 DRM_DESTROY_WAITQUEUE(&file->event_wait);
199 linux_mutex_destroy(&file->fbs_lock);
200}
201
202static void
203drm_master_release(struct drm_file *file)
204{
205
206 /*
207 * XXX I think this locking concept is wrong -- we need to hold
208 * file->master->lock.spinlock across the two calls to
209 * drm_i_have_hw_lock and drm_lock_free.
210 */
211 if (drm_i_have_hw_lock(file->minor->dev, file))
212 drm_lock_free(&file->master->lock,
213 _DRM_LOCKING_CONTEXT(file->master->lock.hw_lock->lock));
214}
215
216static void
217drm_events_release(struct drm_file *file)
218{
219 struct drm_device *const dev = file->minor->dev;
220 struct drm_pending_vblank_event *vblank, *vblank_next;
221 struct drm_pending_event *event, *event_next;
222 unsigned long flags;
223
224 spin_lock_irqsave(&dev->event_lock, flags);
225
226 list_for_each_entry_safe(vblank, vblank_next, &dev->vblank_event_list,
227 base.link) {
228 if (vblank->base.file_priv == file) {
229 list_del(&vblank->base.link);
230 drm_vblank_put(dev, vblank->pipe);
231 (*vblank->base.destroy)(&vblank->base);
232 }
233 }
234 list_for_each_entry_safe(event, event_next, &file->event_list, link) {
235 (*event->destroy)(event);
236 }
237
238 spin_unlock_irqrestore(&dev->event_lock, flags);
239}
240
241static void
242drm_close_file_contexts(struct drm_file *file)
243{
244 struct drm_device *const dev = file->minor->dev;
245 struct drm_ctx_list *node, *next;
246
247 mutex_lock(&dev->ctxlist_mutex);
248 if (!list_empty(&dev->ctxlist)) {
249 list_for_each_entry_safe(node, next, &dev->ctxlist, head) {
250 if (node->tag != file)
251 continue;
252 if (node->handle == DRM_KERNEL_CONTEXT)
253 continue;
254 if (dev->driver->context_dtor)
255 (*dev->driver->context_dtor)(dev,
256 node->handle);
257 drm_ctxbitmap_free(dev, node->handle);
258 list_del(&node->head);
259 kfree(node);
260 }
261 }
262 mutex_unlock(&dev->ctxlist_mutex);
263}
264
265static void
266drm_close_file_master(struct drm_file *file)
267{
268 struct drm_device *const dev = file->minor->dev;
269
270 mutex_lock(&dev->master_mutex);
271 if (file->is_master) {
272 struct drm_file *other_file;
273
274 list_for_each_entry(other_file, &dev->filelist, lhead) {
275 if (other_file == file)
276 continue;
277 if (other_file->master != file->master)
278 continue;
279 other_file->authenticated = 0;
280 }
281 if (file->minor->master == file->master) {
282 if (dev->driver->master_drop)
283 (*dev->driver->master_drop)(dev, file, true);
284 drm_master_put(&file->minor->master);
285 }
286 }
287 if (file->master != NULL)
288 drm_master_put(&file->master);
289 file->is_master = 0;
290 mutex_unlock(&dev->master_mutex);
291}
292