/home/lenb/src/to-linus-stable branch 'acpi-2.6.12'
[linux-2.6] / drivers / char / drm / drm_auth.c
1 /**
2  * \file drm_auth.h 
3  * IOCTLs for authentication
4  *
5  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6  * \author Gareth Hughes <gareth@valinux.com>
7  */
8
9 /*
10  * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
11  *
12  * Copyright 1999 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 #include "drmP.h"
37
38 /**
39  * Generate a hash key from a magic.
40  *
41  * \param magic magic.
42  * \return hash key.
43  *
44  * The key is the modulus of the hash table size, #DRM_HASH_SIZE, which must be
45  * a power of 2.
46  */
47 static int drm_hash_magic(drm_magic_t magic)
48 {
49         return magic & (DRM_HASH_SIZE-1);
50 }
51
52 /**
53  * Find the file with the given magic number.
54  *
55  * \param dev DRM device.
56  * \param magic magic number.
57  *
58  * Searches in drm_device::magiclist within all files with the same hash key
59  * the one with matching magic number, while holding the drm_device::struct_sem
60  * lock.
61  */
62 static drm_file_t *drm_find_file(drm_device_t *dev, drm_magic_t magic)
63 {
64         drm_file_t        *retval = NULL;
65         drm_magic_entry_t *pt;
66         int               hash    = drm_hash_magic(magic);
67
68         down(&dev->struct_sem);
69         for (pt = dev->magiclist[hash].head; pt; pt = pt->next) {
70                 if (pt->magic == magic) {
71                         retval = pt->priv;
72                         break;
73                 }
74         }
75         up(&dev->struct_sem);
76         return retval;
77 }
78
79 /**
80  * Adds a magic number.
81  * 
82  * \param dev DRM device.
83  * \param priv file private data.
84  * \param magic magic number.
85  *
86  * Creates a drm_magic_entry structure and appends to the linked list
87  * associated the magic number hash key in drm_device::magiclist, while holding
88  * the drm_device::struct_sem lock.
89  */
90 static int drm_add_magic(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic)
91 {
92         int               hash;
93         drm_magic_entry_t *entry;
94
95         DRM_DEBUG("%d\n", magic);
96
97         hash         = drm_hash_magic(magic);
98         entry        = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
99         if (!entry) return -ENOMEM;
100         memset(entry, 0, sizeof(*entry));
101         entry->magic = magic;
102         entry->priv  = priv;
103         entry->next  = NULL;
104
105         down(&dev->struct_sem);
106         if (dev->magiclist[hash].tail) {
107                 dev->magiclist[hash].tail->next = entry;
108                 dev->magiclist[hash].tail       = entry;
109         } else {
110                 dev->magiclist[hash].head       = entry;
111                 dev->magiclist[hash].tail       = entry;
112         }
113         up(&dev->struct_sem);
114
115         return 0;
116 }
117
118 /**
119  * Remove a magic number.
120  * 
121  * \param dev DRM device.
122  * \param magic magic number.
123  *
124  * Searches and unlinks the entry in drm_device::magiclist with the magic
125  * number hash key, while holding the drm_device::struct_sem lock.
126  */
127 static int drm_remove_magic(drm_device_t *dev, drm_magic_t magic)
128 {
129         drm_magic_entry_t *prev = NULL;
130         drm_magic_entry_t *pt;
131         int               hash;
132
133
134         DRM_DEBUG("%d\n", magic);
135         hash = drm_hash_magic(magic);
136
137         down(&dev->struct_sem);
138         for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) {
139                 if (pt->magic == magic) {
140                         if (dev->magiclist[hash].head == pt) {
141                                 dev->magiclist[hash].head = pt->next;
142                         }
143                         if (dev->magiclist[hash].tail == pt) {
144                                 dev->magiclist[hash].tail = prev;
145                         }
146                         if (prev) {
147                                 prev->next = pt->next;
148                         }
149                         up(&dev->struct_sem);
150                         return 0;
151                 }
152         }
153         up(&dev->struct_sem);
154
155         drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
156
157         return -EINVAL;
158 }
159
160 /**
161  * Get a unique magic number (ioctl).
162  *
163  * \param inode device inode.
164  * \param filp file pointer.
165  * \param cmd command.
166  * \param arg pointer to a resulting drm_auth structure.
167  * \return zero on success, or a negative number on failure.
168  *
169  * If there is a magic number in drm_file::magic then use it, otherwise
170  * searches an unique non-zero magic number and add it associating it with \p
171  * filp.
172  */
173 int drm_getmagic(struct inode *inode, struct file *filp,
174                   unsigned int cmd, unsigned long arg)
175 {
176         static drm_magic_t sequence = 0;
177         static DEFINE_SPINLOCK(lock);
178         drm_file_t         *priv    = filp->private_data;
179         drm_device_t       *dev     = priv->head->dev;
180         drm_auth_t         auth;
181
182                                 /* Find unique magic */
183         if (priv->magic) {
184                 auth.magic = priv->magic;
185         } else {
186                 do {
187                         spin_lock(&lock);
188                         if (!sequence) ++sequence; /* reserve 0 */
189                         auth.magic = sequence++;
190                         spin_unlock(&lock);
191                 } while (drm_find_file(dev, auth.magic));
192                 priv->magic = auth.magic;
193                 drm_add_magic(dev, priv, auth.magic);
194         }
195
196         DRM_DEBUG("%u\n", auth.magic);
197         if (copy_to_user((drm_auth_t __user *)arg, &auth, sizeof(auth)))
198                 return -EFAULT;
199         return 0;
200 }
201
202 /**
203  * Authenticate with a magic.
204  *
205  * \param inode device inode.
206  * \param filp file pointer.
207  * \param cmd command.
208  * \param arg pointer to a drm_auth structure.
209  * \return zero if authentication successed, or a negative number otherwise.
210  *
211  * Checks if \p filp is associated with the magic number passed in \arg.
212  */
213 int drm_authmagic(struct inode *inode, struct file *filp,
214                    unsigned int cmd, unsigned long arg)
215 {
216         drm_file_t         *priv    = filp->private_data;
217         drm_device_t       *dev     = priv->head->dev;
218         drm_auth_t         auth;
219         drm_file_t         *file;
220
221         if (copy_from_user(&auth, (drm_auth_t __user *)arg, sizeof(auth)))
222                 return -EFAULT;
223         DRM_DEBUG("%u\n", auth.magic);
224         if ((file = drm_find_file(dev, auth.magic))) {
225                 file->authenticated = 1;
226                 drm_remove_magic(dev, auth.magic);
227                 return 0;
228         }
229         return -EINVAL;
230 }