patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / char / drm / drm_context.h
1 /**
2  * \file drm_context.h 
3  * IOCTLs for generic contexts
4  * 
5  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6  * \author Gareth Hughes <gareth@valinux.com>
7  */
8
9 /*
10  * Created: Fri Nov 24 18:31:37 2000 by gareth@valinux.com
11  *
12  * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
13  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
14  * All Rights Reserved.
15  *
16  * Permission is hereby granted, free of charge, to any person obtaining a
17  * copy of this software and associated documentation files (the "Software"),
18  * to deal in the Software without restriction, including without limitation
19  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20  * and/or sell copies of the Software, and to permit persons to whom the
21  * Software is furnished to do so, subject to the following conditions:
22  *
23  * The above copyright notice and this permission notice (including the next
24  * paragraph) shall be included in all copies or substantial portions of the
25  * Software.
26  *
27  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
30  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
31  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
32  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33  * OTHER DEALINGS IN THE SOFTWARE.
34  */
35
36 /*
37  * ChangeLog:
38  *  2001-11-16  Torsten Duwe <duwe@caldera.de>
39  *              added context constructor/destructor hooks,
40  *              needed by SiS driver's memory management.
41  */
42
43 #include "drmP.h"
44
45 #if !__HAVE_CTX_BITMAP
46 #error "__HAVE_CTX_BITMAP must be defined"
47 #endif
48
49
50 /******************************************************************/
51 /** \name Context bitmap support */
52 /*@{*/
53
54 /**
55  * Free a handle from the context bitmap.
56  *
57  * \param dev DRM device.
58  * \param ctx_handle context handle.
59  *
60  * Clears the bit specified by \p ctx_handle in drm_device::ctx_bitmap and the entry
61  * in drm_device::context_sareas, while holding the drm_device::struct_sem
62  * lock.
63  */
64 void DRM(ctxbitmap_free)( drm_device_t *dev, int ctx_handle )
65 {
66         if ( ctx_handle < 0 ) goto failed;
67         if ( !dev->ctx_bitmap ) goto failed;
68
69         if ( ctx_handle < DRM_MAX_CTXBITMAP ) {
70                 down(&dev->struct_sem);
71                 clear_bit( ctx_handle, dev->ctx_bitmap );
72                 dev->context_sareas[ctx_handle] = NULL;
73                 up(&dev->struct_sem);
74                 return;
75         }
76 failed:
77         DRM_ERROR( "Attempt to free invalid context handle: %d\n",
78                    ctx_handle );
79         return;
80 }
81
82 /** 
83  * Context bitmap allocation.
84  *
85  * \param dev DRM device.
86  * \return (non-negative) context handle on success or a negative number on failure.
87  *
88  * Find the first zero bit in drm_device::ctx_bitmap and (re)allocates
89  * drm_device::context_sareas to accommodate the new entry while holding the
90  * drm_device::struct_sem lock.
91  */
92 int DRM(ctxbitmap_next)( drm_device_t *dev )
93 {
94         int bit;
95
96         if(!dev->ctx_bitmap) return -1;
97
98         down(&dev->struct_sem);
99         bit = find_first_zero_bit( dev->ctx_bitmap, DRM_MAX_CTXBITMAP );
100         if ( bit < DRM_MAX_CTXBITMAP ) {
101                 set_bit( bit, dev->ctx_bitmap );
102                 DRM_DEBUG( "drm_ctxbitmap_next bit : %d\n", bit );
103                 if((bit+1) > dev->max_context) {
104                         dev->max_context = (bit+1);
105                         if(dev->context_sareas) {
106                                 drm_map_t **ctx_sareas;
107
108                                 ctx_sareas = DRM(realloc)(dev->context_sareas,
109                                                 (dev->max_context - 1) * 
110                                                 sizeof(*dev->context_sareas),
111                                                 dev->max_context * 
112                                                 sizeof(*dev->context_sareas),
113                                                 DRM_MEM_MAPS);
114                                 if(!ctx_sareas) {
115                                         clear_bit(bit, dev->ctx_bitmap);
116                                         up(&dev->struct_sem);
117                                         return -1;
118                                 }
119                                 dev->context_sareas = ctx_sareas;
120                                 dev->context_sareas[bit] = NULL;
121                         } else {
122                                 /* max_context == 1 at this point */
123                                 dev->context_sareas = DRM(alloc)(
124                                                 dev->max_context * 
125                                                 sizeof(*dev->context_sareas),
126                                                 DRM_MEM_MAPS);
127                                 if(!dev->context_sareas) {
128                                         clear_bit(bit, dev->ctx_bitmap);
129                                         up(&dev->struct_sem);
130                                         return -1;
131                                 }
132                                 dev->context_sareas[bit] = NULL;
133                         }
134                 }
135                 up(&dev->struct_sem);
136                 return bit;
137         }
138         up(&dev->struct_sem);
139         return -1;
140 }
141
142 /**
143  * Context bitmap initialization.
144  *
145  * \param dev DRM device.
146  *
147  * Allocates and initialize drm_device::ctx_bitmap and drm_device::context_sareas, while holding
148  * the drm_device::struct_sem lock.
149  */
150 int DRM(ctxbitmap_init)( drm_device_t *dev )
151 {
152         int i;
153         int temp;
154
155         down(&dev->struct_sem);
156         dev->ctx_bitmap = (unsigned long *) DRM(alloc)( PAGE_SIZE,
157                                                         DRM_MEM_CTXBITMAP );
158         if ( dev->ctx_bitmap == NULL ) {
159                 up(&dev->struct_sem);
160                 return -ENOMEM;
161         }
162         memset( (void *)dev->ctx_bitmap, 0, PAGE_SIZE );
163         dev->context_sareas = NULL;
164         dev->max_context = -1;
165         up(&dev->struct_sem);
166
167         for ( i = 0 ; i < DRM_RESERVED_CONTEXTS ; i++ ) {
168                 temp = DRM(ctxbitmap_next)( dev );
169                 DRM_DEBUG( "drm_ctxbitmap_init : %d\n", temp );
170         }
171
172         return 0;
173 }
174
175 /**
176  * Context bitmap cleanup.
177  *
178  * \param dev DRM device.
179  *
180  * Frees drm_device::ctx_bitmap and drm_device::context_sareas, while holding
181  * the drm_device::struct_sem lock.
182  */
183 void DRM(ctxbitmap_cleanup)( drm_device_t *dev )
184 {
185         down(&dev->struct_sem);
186         if( dev->context_sareas ) DRM(free)( dev->context_sareas,
187                                              sizeof(*dev->context_sareas) * 
188                                              dev->max_context,
189                                              DRM_MEM_MAPS );
190         DRM(free)( (void *)dev->ctx_bitmap, PAGE_SIZE, DRM_MEM_CTXBITMAP );
191         up(&dev->struct_sem);
192 }
193
194 /*@}*/
195
196 /******************************************************************/
197 /** \name Per Context SAREA Support */
198 /*@{*/
199
200 /**
201  * Get per-context SAREA.
202  * 
203  * \param inode device inode.
204  * \param filp file pointer.
205  * \param cmd command.
206  * \param arg user argument pointing to a drm_ctx_priv_map structure.
207  * \return zero on success or a negative number on failure.
208  *
209  * Gets the map from drm_device::context_sareas with the handle specified and
210  * returns its handle.
211  */
212 int DRM(getsareactx)(struct inode *inode, struct file *filp,
213                      unsigned int cmd, unsigned long arg)
214 {
215         drm_file_t      *priv   = filp->private_data;
216         drm_device_t    *dev    = priv->dev;
217         drm_ctx_priv_map_t request;
218         drm_map_t *map;
219
220         if (copy_from_user(&request,
221                            (drm_ctx_priv_map_t *)arg,
222                            sizeof(request)))
223                 return -EFAULT;
224
225         down(&dev->struct_sem);
226         if (dev->max_context < 0 || request.ctx_id >= (unsigned) dev->max_context) {
227                 up(&dev->struct_sem);
228                 return -EINVAL;
229         }
230
231         map = dev->context_sareas[request.ctx_id];
232         up(&dev->struct_sem);
233
234         request.handle = map->handle;
235         if (copy_to_user((drm_ctx_priv_map_t *)arg, &request, sizeof(request)))
236                 return -EFAULT;
237         return 0;
238 }
239
240 /**
241  * Set per-context SAREA.
242  * 
243  * \param inode device inode.
244  * \param filp file pointer.
245  * \param cmd command.
246  * \param arg user argument pointing to a drm_ctx_priv_map structure.
247  * \return zero on success or a negative number on failure.
248  *
249  * Searches the mapping specified in \p arg and update the entry in
250  * drm_device::context_sareas with it.
251  */
252 int DRM(setsareactx)(struct inode *inode, struct file *filp,
253                      unsigned int cmd, unsigned long arg)
254 {
255         drm_file_t      *priv   = filp->private_data;
256         drm_device_t    *dev    = priv->dev;
257         drm_ctx_priv_map_t request;
258         drm_map_t *map = NULL;
259         drm_map_list_t *r_list = NULL;
260         struct list_head *list;
261
262         if (copy_from_user(&request,
263                            (drm_ctx_priv_map_t *)arg,
264                            sizeof(request)))
265                 return -EFAULT;
266
267         down(&dev->struct_sem);
268         list_for_each(list, &dev->maplist->head) {
269                 r_list = list_entry(list, drm_map_list_t, head);
270                 if(r_list->map &&
271                    r_list->map->handle == request.handle)
272                         goto found;
273         }
274 bad:
275         up(&dev->struct_sem);
276         return -EINVAL;
277
278 found:
279         map = r_list->map;
280         if (!map) goto bad;
281         if (dev->max_context < 0)
282                 goto bad;
283         if (request.ctx_id >= (unsigned) dev->max_context)
284                 goto bad;
285         dev->context_sareas[request.ctx_id] = map;
286         up(&dev->struct_sem);
287         return 0;
288 }
289
290 /*@}*/
291
292 /******************************************************************/
293 /** \name The actual DRM context handling routines */
294 /*@{*/
295
296 /**
297  * Switch context.
298  *
299  * \param dev DRM device.
300  * \param old old context handle.
301  * \param new new context handle.
302  * \return zero on success or a negative number on failure.
303  *
304  * Attempt to set drm_device::context_flag.
305  */
306 int DRM(context_switch)( drm_device_t *dev, int old, int new )
307 {
308         if ( test_and_set_bit( 0, &dev->context_flag ) ) {
309                 DRM_ERROR( "Reentering -- FIXME\n" );
310                 return -EBUSY;
311         }
312
313
314         DRM_DEBUG( "Context switch from %d to %d\n", old, new );
315
316         if ( new == dev->last_context ) {
317                 clear_bit( 0, &dev->context_flag );
318                 return 0;
319         }
320
321         return 0;
322 }
323
324 /**
325  * Complete context switch.
326  *
327  * \param dev DRM device.
328  * \param new new context handle.
329  * \return zero on success or a negative number on failure.
330  *
331  * Updates drm_device::last_context and drm_device::last_switch. Verifies the
332  * hardware lock is held, clears the drm_device::context_flag and wakes up
333  * drm_device::context_wait.
334  */
335 int DRM(context_switch_complete)( drm_device_t *dev, int new )
336 {
337         dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
338         dev->last_switch  = jiffies;
339
340         if ( !_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) ) {
341                 DRM_ERROR( "Lock isn't held after context switch\n" );
342         }
343
344                                 /* If a context switch is ever initiated
345                                    when the kernel holds the lock, release
346                                    that lock here. */
347         clear_bit( 0, &dev->context_flag );
348         wake_up( &dev->context_wait );
349
350         return 0;
351 }
352
353 /**
354  * Reserve contexts.
355  *
356  * \param inode device inode.
357  * \param filp file pointer.
358  * \param cmd command.
359  * \param arg user argument pointing to a drm_ctx_res structure.
360  * \return zero on success or a negative number on failure.
361  */
362 int DRM(resctx)( struct inode *inode, struct file *filp,
363                  unsigned int cmd, unsigned long arg )
364 {
365         drm_ctx_res_t res;
366         drm_ctx_t ctx;
367         int i;
368
369         if ( copy_from_user( &res, (drm_ctx_res_t *)arg, sizeof(res) ) )
370                 return -EFAULT;
371
372         if ( res.count >= DRM_RESERVED_CONTEXTS ) {
373                 memset( &ctx, 0, sizeof(ctx) );
374                 for ( i = 0 ; i < DRM_RESERVED_CONTEXTS ; i++ ) {
375                         ctx.handle = i;
376                         if ( copy_to_user( &res.contexts[i],
377                                            &i, sizeof(i) ) )
378                                 return -EFAULT;
379                 }
380         }
381         res.count = DRM_RESERVED_CONTEXTS;
382
383         if ( copy_to_user( (drm_ctx_res_t *)arg, &res, sizeof(res) ) )
384                 return -EFAULT;
385         return 0;
386 }
387
388 /**
389  * Add context.
390  *
391  * \param inode device inode.
392  * \param filp file pointer.
393  * \param cmd command.
394  * \param arg user argument pointing to a drm_ctx structure.
395  * \return zero on success or a negative number on failure.
396  *
397  * Get a new handle for the context and copy to userspace.
398  */
399 int DRM(addctx)( struct inode *inode, struct file *filp,
400                  unsigned int cmd, unsigned long arg )
401 {
402         drm_file_t *priv = filp->private_data;
403         drm_device_t *dev = priv->dev;
404         drm_ctx_list_t * ctx_entry;
405         drm_ctx_t ctx;
406
407         if ( copy_from_user( &ctx, (drm_ctx_t *)arg, sizeof(ctx) ) )
408                 return -EFAULT;
409
410         ctx.handle = DRM(ctxbitmap_next)( dev );
411         if ( ctx.handle == DRM_KERNEL_CONTEXT ) {
412                                 /* Skip kernel's context and get a new one. */
413                 ctx.handle = DRM(ctxbitmap_next)( dev );
414         }
415         DRM_DEBUG( "%d\n", ctx.handle );
416         if ( ctx.handle == -1 ) {
417                 DRM_DEBUG( "Not enough free contexts.\n" );
418                                 /* Should this return -EBUSY instead? */
419                 return -ENOMEM;
420         }
421 #ifdef DRIVER_CTX_CTOR
422         if ( ctx.handle != DRM_KERNEL_CONTEXT )
423                 DRIVER_CTX_CTOR(ctx.handle); /* XXX: also pass dev ? */
424 #endif
425         ctx_entry = DRM(alloc)( sizeof(*ctx_entry), DRM_MEM_CTXLIST );
426         if ( !ctx_entry ) {
427                 DRM_DEBUG("out of memory\n");
428                 return -ENOMEM;
429         }
430
431         INIT_LIST_HEAD( &ctx_entry->head );
432         ctx_entry->handle = ctx.handle;
433         ctx_entry->tag = priv;
434
435         down( &dev->ctxlist_sem );
436         list_add( &ctx_entry->head, &dev->ctxlist->head );
437         ++dev->ctx_count;
438         up( &dev->ctxlist_sem );
439
440         if ( copy_to_user( (drm_ctx_t *)arg, &ctx, sizeof(ctx) ) )
441                 return -EFAULT;
442         return 0;
443 }
444
445 int DRM(modctx)( struct inode *inode, struct file *filp,
446                  unsigned int cmd, unsigned long arg )
447 {
448         /* This does nothing */
449         return 0;
450 }
451
452 /**
453  * Get context.
454  *
455  * \param inode device inode.
456  * \param filp file pointer.
457  * \param cmd command.
458  * \param arg user argument pointing to a drm_ctx structure.
459  * \return zero on success or a negative number on failure.
460  */
461 int DRM(getctx)( struct inode *inode, struct file *filp,
462                  unsigned int cmd, unsigned long arg )
463 {
464         drm_ctx_t ctx;
465
466         if ( copy_from_user( &ctx, (drm_ctx_t*)arg, sizeof(ctx) ) )
467                 return -EFAULT;
468
469         /* This is 0, because we don't handle any context flags */
470         ctx.flags = 0;
471
472         if ( copy_to_user( (drm_ctx_t*)arg, &ctx, sizeof(ctx) ) )
473                 return -EFAULT;
474         return 0;
475 }
476
477 /**
478  * Switch context.
479  *
480  * \param inode device inode.
481  * \param filp file pointer.
482  * \param cmd command.
483  * \param arg user argument pointing to a drm_ctx structure.
484  * \return zero on success or a negative number on failure.
485  *
486  * Calls context_switch().
487  */
488 int DRM(switchctx)( struct inode *inode, struct file *filp,
489                     unsigned int cmd, unsigned long arg )
490 {
491         drm_file_t *priv = filp->private_data;
492         drm_device_t *dev = priv->dev;
493         drm_ctx_t ctx;
494
495         if ( copy_from_user( &ctx, (drm_ctx_t *)arg, sizeof(ctx) ) )
496                 return -EFAULT;
497
498         DRM_DEBUG( "%d\n", ctx.handle );
499         return DRM(context_switch)( dev, dev->last_context, ctx.handle );
500 }
501
502 /**
503  * New context.
504  *
505  * \param inode device inode.
506  * \param filp file pointer.
507  * \param cmd command.
508  * \param arg user argument pointing to a drm_ctx structure.
509  * \return zero on success or a negative number on failure.
510  *
511  * Calls context_switch_complete().
512  */
513 int DRM(newctx)( struct inode *inode, struct file *filp,
514                  unsigned int cmd, unsigned long arg )
515 {
516         drm_file_t *priv = filp->private_data;
517         drm_device_t *dev = priv->dev;
518         drm_ctx_t ctx;
519
520         if ( copy_from_user( &ctx, (drm_ctx_t *)arg, sizeof(ctx) ) )
521                 return -EFAULT;
522
523         DRM_DEBUG( "%d\n", ctx.handle );
524         DRM(context_switch_complete)( dev, ctx.handle );
525
526         return 0;
527 }
528
529 /**
530  * Remove context.
531  *
532  * \param inode device inode.
533  * \param filp file pointer.
534  * \param cmd command.
535  * \param arg user argument pointing to a drm_ctx structure.
536  * \return zero on success or a negative number on failure.
537  *
538  * If not the special kernel context, calls ctxbitmap_free() to free the specified context.
539  */
540 int DRM(rmctx)( struct inode *inode, struct file *filp,
541                 unsigned int cmd, unsigned long arg )
542 {
543         drm_file_t *priv = filp->private_data;
544         drm_device_t *dev = priv->dev;
545         drm_ctx_t ctx;
546
547         if ( copy_from_user( &ctx, (drm_ctx_t *)arg, sizeof(ctx) ) )
548                 return -EFAULT;
549
550         DRM_DEBUG( "%d\n", ctx.handle );
551         if ( ctx.handle == DRM_KERNEL_CONTEXT + 1 ) {
552                 priv->remove_auth_on_close = 1;
553         }
554         if ( ctx.handle != DRM_KERNEL_CONTEXT ) {
555 #ifdef DRIVER_CTX_DTOR
556                 DRIVER_CTX_DTOR(ctx.handle); /* XXX: also pass dev ? */
557 #endif
558                 DRM(ctxbitmap_free)( dev, ctx.handle );
559         }
560
561         down( &dev->ctxlist_sem );
562         if ( !list_empty( &dev->ctxlist->head ) ) {
563                 drm_ctx_list_t *pos, *n;
564
565                 list_for_each_entry_safe( pos, n, &dev->ctxlist->head, head ) {
566                         if ( pos->handle == ctx.handle ) {
567                                 list_del( &pos->head );
568                                 DRM(free)( pos, sizeof(*pos), DRM_MEM_CTXLIST );
569                                 --dev->ctx_count;
570                         }
571                 }
572         }
573         up( &dev->ctxlist_sem );
574
575         return 0;
576 }
577
578 /*@}*/