diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/drm/drm_fops.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/drm/drm_fops.c')
-rw-r--r-- | drivers/char/drm/drm_fops.c | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/drivers/char/drm/drm_fops.c b/drivers/char/drm/drm_fops.c new file mode 100644 index 000000000000..906794247aeb --- /dev/null +++ b/drivers/char/drm/drm_fops.c | |||
@@ -0,0 +1,451 @@ | |||
1 | /** | ||
2 | * \file drm_fops.h | ||
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 | |||
37 | #include "drmP.h" | ||
38 | #include <linux/poll.h> | ||
39 | |||
40 | static int drm_setup( drm_device_t *dev ) | ||
41 | { | ||
42 | int i; | ||
43 | int ret; | ||
44 | |||
45 | if (dev->driver->presetup) | ||
46 | { | ||
47 | ret=dev->driver->presetup(dev); | ||
48 | if (ret!=0) | ||
49 | return ret; | ||
50 | } | ||
51 | |||
52 | atomic_set( &dev->ioctl_count, 0 ); | ||
53 | atomic_set( &dev->vma_count, 0 ); | ||
54 | dev->buf_use = 0; | ||
55 | atomic_set( &dev->buf_alloc, 0 ); | ||
56 | |||
57 | if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) | ||
58 | { | ||
59 | i = drm_dma_setup( dev ); | ||
60 | if ( i < 0 ) | ||
61 | return i; | ||
62 | } | ||
63 | |||
64 | for ( i = 0 ; i < DRM_ARRAY_SIZE(dev->counts) ; i++ ) | ||
65 | atomic_set( &dev->counts[i], 0 ); | ||
66 | |||
67 | for ( i = 0 ; i < DRM_HASH_SIZE ; i++ ) { | ||
68 | dev->magiclist[i].head = NULL; | ||
69 | dev->magiclist[i].tail = NULL; | ||
70 | } | ||
71 | |||
72 | dev->maplist = drm_alloc(sizeof(*dev->maplist), | ||
73 | DRM_MEM_MAPS); | ||
74 | if(dev->maplist == NULL) return -ENOMEM; | ||
75 | memset(dev->maplist, 0, sizeof(*dev->maplist)); | ||
76 | INIT_LIST_HEAD(&dev->maplist->head); | ||
77 | |||
78 | dev->ctxlist = drm_alloc(sizeof(*dev->ctxlist), | ||
79 | DRM_MEM_CTXLIST); | ||
80 | if(dev->ctxlist == NULL) return -ENOMEM; | ||
81 | memset(dev->ctxlist, 0, sizeof(*dev->ctxlist)); | ||
82 | INIT_LIST_HEAD(&dev->ctxlist->head); | ||
83 | |||
84 | dev->vmalist = NULL; | ||
85 | dev->sigdata.lock = dev->lock.hw_lock = NULL; | ||
86 | init_waitqueue_head( &dev->lock.lock_queue ); | ||
87 | dev->queue_count = 0; | ||
88 | dev->queue_reserved = 0; | ||
89 | dev->queue_slots = 0; | ||
90 | dev->queuelist = NULL; | ||
91 | dev->irq_enabled = 0; | ||
92 | dev->context_flag = 0; | ||
93 | dev->interrupt_flag = 0; | ||
94 | dev->dma_flag = 0; | ||
95 | dev->last_context = 0; | ||
96 | dev->last_switch = 0; | ||
97 | dev->last_checked = 0; | ||
98 | init_waitqueue_head( &dev->context_wait ); | ||
99 | dev->if_version = 0; | ||
100 | |||
101 | dev->ctx_start = 0; | ||
102 | dev->lck_start = 0; | ||
103 | |||
104 | dev->buf_rp = dev->buf; | ||
105 | dev->buf_wp = dev->buf; | ||
106 | dev->buf_end = dev->buf + DRM_BSZ; | ||
107 | dev->buf_async = NULL; | ||
108 | init_waitqueue_head( &dev->buf_readers ); | ||
109 | init_waitqueue_head( &dev->buf_writers ); | ||
110 | |||
111 | DRM_DEBUG( "\n" ); | ||
112 | |||
113 | /* | ||
114 | * The kernel's context could be created here, but is now created | ||
115 | * in drm_dma_enqueue. This is more resource-efficient for | ||
116 | * hardware that does not do DMA, but may mean that | ||
117 | * drm_select_queue fails between the time the interrupt is | ||
118 | * initialized and the time the queues are initialized. | ||
119 | */ | ||
120 | if (dev->driver->postsetup) | ||
121 | dev->driver->postsetup(dev); | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | /** | ||
127 | * Open file. | ||
128 | * | ||
129 | * \param inode device inode | ||
130 | * \param filp file pointer. | ||
131 | * \return zero on success or a negative number on failure. | ||
132 | * | ||
133 | * Searches the DRM device with the same minor number, calls open_helper(), and | ||
134 | * increments the device open count. If the open count was previous at zero, | ||
135 | * i.e., it's the first that the device is open, then calls setup(). | ||
136 | */ | ||
137 | int drm_open( struct inode *inode, struct file *filp ) | ||
138 | { | ||
139 | drm_device_t *dev = NULL; | ||
140 | int minor = iminor(inode); | ||
141 | int retcode = 0; | ||
142 | |||
143 | if (!((minor >= 0) && (minor < drm_cards_limit))) | ||
144 | return -ENODEV; | ||
145 | |||
146 | if (!drm_heads[minor]) | ||
147 | return -ENODEV; | ||
148 | |||
149 | if (!(dev = drm_heads[minor]->dev)) | ||
150 | return -ENODEV; | ||
151 | |||
152 | retcode = drm_open_helper( inode, filp, dev ); | ||
153 | if ( !retcode ) { | ||
154 | atomic_inc( &dev->counts[_DRM_STAT_OPENS] ); | ||
155 | spin_lock( &dev->count_lock ); | ||
156 | if ( !dev->open_count++ ) { | ||
157 | spin_unlock( &dev->count_lock ); | ||
158 | return drm_setup( dev ); | ||
159 | } | ||
160 | spin_unlock( &dev->count_lock ); | ||
161 | } | ||
162 | |||
163 | return retcode; | ||
164 | } | ||
165 | EXPORT_SYMBOL(drm_open); | ||
166 | |||
167 | /** | ||
168 | * Release file. | ||
169 | * | ||
170 | * \param inode device inode | ||
171 | * \param filp file pointer. | ||
172 | * \return zero on success or a negative number on failure. | ||
173 | * | ||
174 | * If the hardware lock is held then free it, and take it again for the kernel | ||
175 | * context since it's necessary to reclaim buffers. Unlink the file private | ||
176 | * data from its list and free it. Decreases the open count and if it reaches | ||
177 | * zero calls takedown(). | ||
178 | */ | ||
179 | int drm_release( struct inode *inode, struct file *filp ) | ||
180 | { | ||
181 | drm_file_t *priv = filp->private_data; | ||
182 | drm_device_t *dev; | ||
183 | int retcode = 0; | ||
184 | |||
185 | lock_kernel(); | ||
186 | dev = priv->head->dev; | ||
187 | |||
188 | DRM_DEBUG( "open_count = %d\n", dev->open_count ); | ||
189 | |||
190 | if (dev->driver->prerelease) | ||
191 | dev->driver->prerelease(dev, filp); | ||
192 | |||
193 | /* ======================================================== | ||
194 | * Begin inline drm_release | ||
195 | */ | ||
196 | |||
197 | DRM_DEBUG( "pid = %d, device = 0x%lx, open_count = %d\n", | ||
198 | current->pid, (long)old_encode_dev(priv->head->device), dev->open_count ); | ||
199 | |||
200 | if ( priv->lock_count && dev->lock.hw_lock && | ||
201 | _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) && | ||
202 | dev->lock.filp == filp ) { | ||
203 | DRM_DEBUG( "File %p released, freeing lock for context %d\n", | ||
204 | filp, | ||
205 | _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) ); | ||
206 | |||
207 | if (dev->driver->release) | ||
208 | dev->driver->release(dev, filp); | ||
209 | |||
210 | drm_lock_free( dev, &dev->lock.hw_lock->lock, | ||
211 | _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) ); | ||
212 | |||
213 | /* FIXME: may require heavy-handed reset of | ||
214 | hardware at this point, possibly | ||
215 | processed via a callback to the X | ||
216 | server. */ | ||
217 | } | ||
218 | else if ( dev->driver->release && priv->lock_count && dev->lock.hw_lock ) { | ||
219 | /* The lock is required to reclaim buffers */ | ||
220 | DECLARE_WAITQUEUE( entry, current ); | ||
221 | |||
222 | add_wait_queue( &dev->lock.lock_queue, &entry ); | ||
223 | for (;;) { | ||
224 | __set_current_state(TASK_INTERRUPTIBLE); | ||
225 | if ( !dev->lock.hw_lock ) { | ||
226 | /* Device has been unregistered */ | ||
227 | retcode = -EINTR; | ||
228 | break; | ||
229 | } | ||
230 | if ( drm_lock_take( &dev->lock.hw_lock->lock, | ||
231 | DRM_KERNEL_CONTEXT ) ) { | ||
232 | dev->lock.filp = filp; | ||
233 | dev->lock.lock_time = jiffies; | ||
234 | atomic_inc( &dev->counts[_DRM_STAT_LOCKS] ); | ||
235 | break; /* Got lock */ | ||
236 | } | ||
237 | /* Contention */ | ||
238 | schedule(); | ||
239 | if ( signal_pending( current ) ) { | ||
240 | retcode = -ERESTARTSYS; | ||
241 | break; | ||
242 | } | ||
243 | } | ||
244 | __set_current_state(TASK_RUNNING); | ||
245 | remove_wait_queue( &dev->lock.lock_queue, &entry ); | ||
246 | if( !retcode ) { | ||
247 | if (dev->driver->release) | ||
248 | dev->driver->release(dev, filp); | ||
249 | drm_lock_free( dev, &dev->lock.hw_lock->lock, | ||
250 | DRM_KERNEL_CONTEXT ); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) | ||
255 | { | ||
256 | dev->driver->reclaim_buffers(dev, filp); | ||
257 | } | ||
258 | |||
259 | drm_fasync( -1, filp, 0 ); | ||
260 | |||
261 | down( &dev->ctxlist_sem ); | ||
262 | if ( !list_empty( &dev->ctxlist->head ) ) { | ||
263 | drm_ctx_list_t *pos, *n; | ||
264 | |||
265 | list_for_each_entry_safe( pos, n, &dev->ctxlist->head, head ) { | ||
266 | if ( pos->tag == priv && | ||
267 | pos->handle != DRM_KERNEL_CONTEXT ) { | ||
268 | if (dev->driver->context_dtor) | ||
269 | dev->driver->context_dtor(dev, pos->handle); | ||
270 | |||
271 | drm_ctxbitmap_free( dev, pos->handle ); | ||
272 | |||
273 | list_del( &pos->head ); | ||
274 | drm_free( pos, sizeof(*pos), DRM_MEM_CTXLIST ); | ||
275 | --dev->ctx_count; | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | up( &dev->ctxlist_sem ); | ||
280 | |||
281 | down( &dev->struct_sem ); | ||
282 | if ( priv->remove_auth_on_close == 1 ) { | ||
283 | drm_file_t *temp = dev->file_first; | ||
284 | while ( temp ) { | ||
285 | temp->authenticated = 0; | ||
286 | temp = temp->next; | ||
287 | } | ||
288 | } | ||
289 | if ( priv->prev ) { | ||
290 | priv->prev->next = priv->next; | ||
291 | } else { | ||
292 | dev->file_first = priv->next; | ||
293 | } | ||
294 | if ( priv->next ) { | ||
295 | priv->next->prev = priv->prev; | ||
296 | } else { | ||
297 | dev->file_last = priv->prev; | ||
298 | } | ||
299 | up( &dev->struct_sem ); | ||
300 | |||
301 | if (dev->driver->free_filp_priv) | ||
302 | dev->driver->free_filp_priv(dev, priv); | ||
303 | |||
304 | drm_free( priv, sizeof(*priv), DRM_MEM_FILES ); | ||
305 | |||
306 | /* ======================================================== | ||
307 | * End inline drm_release | ||
308 | */ | ||
309 | |||
310 | atomic_inc( &dev->counts[_DRM_STAT_CLOSES] ); | ||
311 | spin_lock( &dev->count_lock ); | ||
312 | if ( !--dev->open_count ) { | ||
313 | if ( atomic_read( &dev->ioctl_count ) || dev->blocked ) { | ||
314 | DRM_ERROR( "Device busy: %d %d\n", | ||
315 | atomic_read( &dev->ioctl_count ), | ||
316 | dev->blocked ); | ||
317 | spin_unlock( &dev->count_lock ); | ||
318 | unlock_kernel(); | ||
319 | return -EBUSY; | ||
320 | } | ||
321 | spin_unlock( &dev->count_lock ); | ||
322 | unlock_kernel(); | ||
323 | return drm_takedown( dev ); | ||
324 | } | ||
325 | spin_unlock( &dev->count_lock ); | ||
326 | |||
327 | unlock_kernel(); | ||
328 | |||
329 | return retcode; | ||
330 | } | ||
331 | EXPORT_SYMBOL(drm_release); | ||
332 | |||
333 | /** | ||
334 | * Called whenever a process opens /dev/drm. | ||
335 | * | ||
336 | * \param inode device inode. | ||
337 | * \param filp file pointer. | ||
338 | * \param dev device. | ||
339 | * \return zero on success or a negative number on failure. | ||
340 | * | ||
341 | * Creates and initializes a drm_file structure for the file private data in \p | ||
342 | * filp and add it into the double linked list in \p dev. | ||
343 | */ | ||
344 | int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev) | ||
345 | { | ||
346 | int minor = iminor(inode); | ||
347 | drm_file_t *priv; | ||
348 | int ret; | ||
349 | |||
350 | if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */ | ||
351 | if (!drm_cpu_valid()) return -EINVAL; | ||
352 | |||
353 | DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor); | ||
354 | |||
355 | priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES); | ||
356 | if(!priv) return -ENOMEM; | ||
357 | |||
358 | memset(priv, 0, sizeof(*priv)); | ||
359 | filp->private_data = priv; | ||
360 | priv->uid = current->euid; | ||
361 | priv->pid = current->pid; | ||
362 | priv->minor = minor; | ||
363 | priv->head = drm_heads[minor]; | ||
364 | priv->ioctl_count = 0; | ||
365 | priv->authenticated = capable(CAP_SYS_ADMIN); | ||
366 | priv->lock_count = 0; | ||
367 | |||
368 | if (dev->driver->open_helper) { | ||
369 | ret=dev->driver->open_helper(dev, priv); | ||
370 | if (ret < 0) | ||
371 | goto out_free; | ||
372 | } | ||
373 | |||
374 | down(&dev->struct_sem); | ||
375 | if (!dev->file_last) { | ||
376 | priv->next = NULL; | ||
377 | priv->prev = NULL; | ||
378 | dev->file_first = priv; | ||
379 | dev->file_last = priv; | ||
380 | } else { | ||
381 | priv->next = NULL; | ||
382 | priv->prev = dev->file_last; | ||
383 | dev->file_last->next = priv; | ||
384 | dev->file_last = priv; | ||
385 | } | ||
386 | up(&dev->struct_sem); | ||
387 | |||
388 | #ifdef __alpha__ | ||
389 | /* | ||
390 | * Default the hose | ||
391 | */ | ||
392 | if (!dev->hose) { | ||
393 | struct pci_dev *pci_dev; | ||
394 | pci_dev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, NULL); | ||
395 | if (pci_dev) { | ||
396 | dev->hose = pci_dev->sysdata; | ||
397 | pci_dev_put(pci_dev); | ||
398 | } | ||
399 | if (!dev->hose) { | ||
400 | struct pci_bus *b = pci_bus_b(pci_root_buses.next); | ||
401 | if (b) dev->hose = b->sysdata; | ||
402 | } | ||
403 | } | ||
404 | #endif | ||
405 | |||
406 | return 0; | ||
407 | out_free: | ||
408 | drm_free(priv, sizeof(*priv), DRM_MEM_FILES); | ||
409 | filp->private_data=NULL; | ||
410 | return ret; | ||
411 | } | ||
412 | |||
413 | /** No-op. */ | ||
414 | int drm_flush(struct file *filp) | ||
415 | { | ||
416 | drm_file_t *priv = filp->private_data; | ||
417 | drm_device_t *dev = priv->head->dev; | ||
418 | |||
419 | DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", | ||
420 | current->pid, (long)old_encode_dev(priv->head->device), dev->open_count); | ||
421 | return 0; | ||
422 | } | ||
423 | EXPORT_SYMBOL(drm_flush); | ||
424 | |||
425 | /** No-op. */ | ||
426 | int drm_fasync(int fd, struct file *filp, int on) | ||
427 | { | ||
428 | drm_file_t *priv = filp->private_data; | ||
429 | drm_device_t *dev = priv->head->dev; | ||
430 | int retcode; | ||
431 | |||
432 | DRM_DEBUG("fd = %d, device = 0x%lx\n", fd, (long)old_encode_dev(priv->head->device)); | ||
433 | retcode = fasync_helper(fd, filp, on, &dev->buf_async); | ||
434 | if (retcode < 0) return retcode; | ||
435 | return 0; | ||
436 | } | ||
437 | EXPORT_SYMBOL(drm_fasync); | ||
438 | |||
439 | /** No-op. */ | ||
440 | unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) | ||
441 | { | ||
442 | return 0; | ||
443 | } | ||
444 | EXPORT_SYMBOL(drm_poll); | ||
445 | |||
446 | |||
447 | /** No-op. */ | ||
448 | ssize_t drm_read(struct file *filp, char __user *buf, size_t count, loff_t *off) | ||
449 | { | ||
450 | return 0; | ||
451 | } | ||