Merge branch 'master'
[linux-2.6] / drivers / char / drm / drm_auth.c
1 /**
2  * \file drm_auth.c
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,
91                          drm_magic_t magic)
92 {
93         int hash;
94         drm_magic_entry_t *entry;
95
96         DRM_DEBUG("%d\n", magic);
97
98         hash = drm_hash_magic(magic);
99         entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
100         if (!entry)
101                 return -ENOMEM;
102         memset(entry, 0, sizeof(*entry));
103         entry->magic = magic;
104         entry->priv = priv;
105         entry->next = NULL;
106
107         down(&dev->struct_sem);
108         if (dev->magiclist[hash].tail) {
109                 dev->magiclist[hash].tail->next = entry;
110                 dev->magiclist[hash].tail = entry;
111         } else {
112                 dev->magiclist[hash].head = entry;
113                 dev->magiclist[hash].tail = entry;
114         }
115         up(&dev->struct_sem);
116
117         return 0;
118 }
119
120 /**
121  * Remove a magic number.
122  *
123  * \param dev DRM device.
124  * \param magic magic number.
125  *
126  * Searches and unlinks the entry in drm_device::magiclist with the magic
127  * number hash key, while holding the drm_device::struct_sem lock.
128  */
129 static int drm_remove_magic(drm_device_t * dev, drm_magic_t magic)
130 {
131         drm_magic_entry_t *prev = NULL;
132         drm_magic_entry_t *pt;
133         int hash;
134
135         DRM_DEBUG("%d\n", magic);
136         hash = drm_hash_magic(magic);
137
138         down(&dev->struct_sem);
139         for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) {
140                 if (pt->magic == magic) {
141                         if (dev->magiclist[hash].head == pt) {
142                                 dev->magiclist[hash].head = pt->next;
143                         }
144                         if (dev->magiclist[hash].tail == pt) {
145                                 dev->magiclist[hash].tail = prev;
146                         }
147                         if (prev) {
148                                 prev->next = pt->next;
149                         }
150                         up(&dev->struct_sem);
151                         return 0;
152                 }
153         }
154         up(&dev->struct_sem);
155
156         drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
157
158         return -EINVAL;
159 }
160
161 /**
162  * Get a unique magic number (ioctl).
163  *
164  * \param inode device inode.
165  * \param filp file pointer.
166  * \param cmd command.
167  * \param arg pointer to a resulting drm_auth structure.
168  * \return zero on success, or a negative number on failure.
169  *
170  * If there is a magic number in drm_file::magic then use it, otherwise
171  * searches an unique non-zero magic number and add it associating it with \p
172  * filp.
173  */
174 int drm_getmagic(struct inode *inode, struct file *filp,
175                  unsigned int cmd, unsigned long arg)
176 {
177         static drm_magic_t sequence = 0;
178         static DEFINE_SPINLOCK(lock);
179         drm_file_t *priv = filp->private_data;
180         drm_device_t *dev = priv->head->dev;
181         drm_auth_t auth;
182
183         /* Find unique magic */
184         if (priv->magic) {
185                 auth.magic = priv->magic;
186         } else {
187                 do {
188                         spin_lock(&lock);
189                         if (!sequence)
190                                 ++sequence;     /* reserve 0 */
191                         auth.magic = sequence++;
192                         spin_unlock(&lock);
193                 } while (drm_find_file(dev, auth.magic));
194                 priv->magic = auth.magic;
195                 drm_add_magic(dev, priv, auth.magic);
196         }
197
198         DRM_DEBUG("%u\n", auth.magic);
199         if (copy_to_user((drm_auth_t __user *) arg, &auth, sizeof(auth)))
200                 return -EFAULT;
201         return 0;
202 }
203
204 /**
205  * Authenticate with a magic.
206  *
207  * \param inode device inode.
208  * \param filp file pointer.
209  * \param cmd command.
210  * \param arg pointer to a drm_auth structure.
211  * \return zero if authentication successed, or a negative number otherwise.
212  *
213  * Checks if \p filp is associated with the magic number passed in \arg.
214  */
215 int drm_authmagic(struct inode *inode, struct file *filp,
216                   unsigned int cmd, unsigned long arg)
217 {
218         drm_file_t *priv = filp->private_data;
219         drm_device_t *dev = priv->head->dev;
220         drm_auth_t auth;
221         drm_file_t *file;
222
223         if (copy_from_user(&auth, (drm_auth_t __user *) arg, sizeof(auth)))
224                 return -EFAULT;
225         DRM_DEBUG("%u\n", auth.magic);
226         if ((file = drm_find_file(dev, auth.magic))) {
227                 file->authenticated = 1;
228                 drm_remove_magic(dev, auth.magic);
229                 return 0;
230         }
231         return -EINVAL;
232 }