This commit was generated by cvs2svn to compensate for changes in r2,
[linux-2.6.git] / drivers / char / drm / drm_stub.h
1 /**
2  * \file drm_stub.h
3  * Stub support
4  *
5  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6  */
7
8 /*
9  * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org
10  *
11  * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California.
12  * All Rights Reserved.
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the "Software"),
16  * to deal in the Software without restriction, including without limitation
17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18  * and/or sell copies of the Software, and to permit persons to whom the
19  * Software is furnished to do so, subject to the following conditions:
20  *
21  * The above copyright notice and this permission notice (including the next
22  * paragraph) shall be included in all copies or substantial portions of the
23  * Software.
24  *
25  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28  * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
29  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
30  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31  * DEALINGS IN THE SOFTWARE.
32  */
33
34 #include "drmP.h"
35
36 #define DRM_STUB_MAXCARDS 16    /* Enough for one machine */
37
38 static struct class_simple *drm_class;
39
40 /** Stub list. One for each minor. */
41 static struct drm_stub_list {
42         const char             *name;
43         struct file_operations *fops;   /**< file operations */
44         struct proc_dir_entry  *dev_root;       /**< proc directory entry */
45 } *DRM(stub_list);
46
47 static struct proc_dir_entry *DRM(stub_root);
48
49 /** Stub information */
50 static struct drm_stub_info {
51         int (*info_register)(const char *name, struct file_operations *fops,
52                              drm_device_t *dev);
53         int (*info_unregister)(int minor);
54 } DRM(stub_info);
55
56 /**
57  * File \c open operation.
58  *
59  * \param inode device inode.
60  * \param filp file pointer.
61  *
62  * Puts the drm_stub_list::fops corresponding to the device minor number into
63  * \p filp, call the \c open method, and restore the file operations.
64  */
65 static int DRM(stub_open)(struct inode *inode, struct file *filp)
66 {
67         int                    minor = iminor(inode);
68         int                    err   = -ENODEV;
69         struct file_operations *old_fops;
70
71         if (!DRM(stub_list) || !DRM(stub_list)[minor].fops) return -ENODEV;
72         old_fops   = filp->f_op;
73         filp->f_op = fops_get(DRM(stub_list)[minor].fops);
74         if (filp->f_op->open && (err = filp->f_op->open(inode, filp))) {
75                 fops_put(filp->f_op);
76                 filp->f_op = fops_get(old_fops);
77         }
78         fops_put(old_fops);
79
80         return err;
81 }
82
83 /** File operations structure */
84 static struct file_operations DRM(stub_fops) = {
85         .owner = THIS_MODULE,
86         .open  = DRM(stub_open)
87 };
88
89 /**
90  * Get a device minor number.
91  *
92  * \param name driver name.
93  * \param fops file operations.
94  * \param dev DRM device.
95  * \return minor number on success, or a negative number on failure.
96  *
97  * Allocate and initialize ::stub_list if one doesn't exist already.  Search an
98  * empty entry and initialize it to the given parameters, and create the proc
99  * init entry via proc_init().
100  */
101 static int DRM(stub_getminor)(const char *name, struct file_operations *fops,
102                               drm_device_t *dev)
103 {
104         int i;
105
106         if (!DRM(stub_list)) {
107                 DRM(stub_list) = DRM(alloc)(sizeof(*DRM(stub_list))
108                                             * DRM_STUB_MAXCARDS, DRM_MEM_STUB);
109                 if(!DRM(stub_list)) return -1;
110                 for (i = 0; i < DRM_STUB_MAXCARDS; i++) {
111                         DRM(stub_list)[i].name = NULL;
112                         DRM(stub_list)[i].fops = NULL;
113                 }
114         }
115         for (i = 0; i < DRM_STUB_MAXCARDS; i++) {
116                 if (!DRM(stub_list)[i].fops) {
117                         DRM(stub_list)[i].name = name;
118                         DRM(stub_list)[i].fops = fops;
119                         DRM(stub_root) = DRM(proc_init)(dev, i, DRM(stub_root),
120                                                         &DRM(stub_list)[i]
121                                                         .dev_root);
122                         class_simple_device_add(drm_class, MKDEV(DRM_MAJOR, i), NULL, name);
123                         return i;
124                 }
125         }
126         return -1;
127 }
128
129 /**
130  * Put a device minor number.
131  *
132  * \param minor minor number.
133  * \return always zero.
134  *
135  * Cleans up the proc resources. If a minor is zero then release the foreign
136  * "drm" data, otherwise unregisters the "drm" data, frees the stub list and
137  * unregisters the character device. 
138  */
139 static int DRM(stub_putminor)(int minor)
140 {
141         if (minor < 0 || minor >= DRM_STUB_MAXCARDS) return -1;
142         DRM(stub_list)[minor].name = NULL;
143         DRM(stub_list)[minor].fops = NULL;
144         DRM(proc_cleanup)(minor, DRM(stub_root),
145                           DRM(stub_list)[minor].dev_root);
146         if (minor) {
147                 class_simple_device_remove(MKDEV(DRM_MAJOR, minor));
148                 inter_module_put("drm");
149         } else {
150                 inter_module_unregister("drm");
151                 DRM(free)(DRM(stub_list),
152                           sizeof(*DRM(stub_list)) * DRM_STUB_MAXCARDS,
153                           DRM_MEM_STUB);
154                 unregister_chrdev(DRM_MAJOR, "drm");
155                 class_simple_device_remove(MKDEV(DRM_MAJOR, minor));
156                 class_simple_destroy(drm_class);
157         }
158         return 0;
159 }
160
161 /**
162  * Register.
163  *
164  * \param name driver name.
165  * \param fops file operations
166  * \param dev DRM device.
167  * \return zero on success or a negative number on failure.
168  *
169  * Attempt to register the char device and get the foreign "drm" data. If
170  * successful then another module already registered so gets the stub info,
171  * otherwise use this module stub info and make it available for other modules.
172  *
173  * Finally calls stub_info::info_register.
174  */
175 int DRM(stub_register)(const char *name, struct file_operations *fops,
176                        drm_device_t *dev)
177 {
178         struct drm_stub_info *i = NULL;
179         int ret1;
180         int ret2;
181
182         DRM_DEBUG("\n");
183         ret1 = register_chrdev(DRM_MAJOR, "drm", &DRM(stub_fops));
184         if (!ret1) {
185                 drm_class = class_simple_create(THIS_MODULE, "drm");
186                 if (IS_ERR(drm_class)) {
187                         printk (KERN_ERR "Error creating drm class.\n");
188                         unregister_chrdev(DRM_MAJOR, "drm");
189                         return PTR_ERR(drm_class);
190                 }
191         }
192         else if (ret1 == -EBUSY)
193                 i = (struct drm_stub_info *)inter_module_get("drm");
194         else
195                 return -1;
196
197         if (i) {
198                                 /* Already registered */
199                 DRM(stub_info).info_register   = i->info_register;
200                 DRM(stub_info).info_unregister = i->info_unregister;
201                 DRM_DEBUG("already registered\n");
202         } else if (DRM(stub_info).info_register != DRM(stub_getminor)) {
203                 DRM(stub_info).info_register   = DRM(stub_getminor);
204                 DRM(stub_info).info_unregister = DRM(stub_putminor);
205                 DRM_DEBUG("calling inter_module_register\n");
206                 inter_module_register("drm", THIS_MODULE, &DRM(stub_info));
207         }
208         if (DRM(stub_info).info_register) {
209                 ret2 = DRM(stub_info).info_register(name, fops, dev);
210                 if (ret2) {
211                         if (!ret1) {
212                         unregister_chrdev(DRM_MAJOR, "drm");
213                         class_simple_destroy(drm_class);
214                         }
215                         if (!i)
216                                 inter_module_unregister("drm");
217                 }
218                 return ret2;
219         }
220         return -1;
221 }
222
223 /**
224  * Unregister.
225  *
226  * \param minor
227  *
228  * Calls drm_stub_info::unregister.
229  */
230 int DRM(stub_unregister)(int minor)
231 {
232         DRM_DEBUG("%d\n", minor);
233         if (DRM(stub_info).info_unregister)
234                 return DRM(stub_info).info_unregister(minor);
235         return -1;
236 }