DEVFS - Modification of clone behaviour; general cleanup; xtime
[dragonfly.git] / sys / kern / kern_conf.c
1 /*-
2  * Parts Copyright (c) 1995 Terrence R. Lambert
3  * Copyright (c) 1995 Julian R. Elischer
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by Terrence R. Lambert.
17  * 4. The name Terrence R. Lambert may not be used to endorse or promote
18  *    products derived from this software without specific prior written
19  *    permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY Julian R. Elischer ``AS IS'' AND ANY
22  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD: src/sys/kern/kern_conf.c,v 1.73.2.3 2003/03/10 02:18:25 imp Exp $
34  * $DragonFly: src/sys/kern/kern_conf.c,v 1.23 2007/05/09 00:53:34 dillon Exp $
35  */
36
37 #include <sys/param.h>
38 #include <sys/kernel.h>
39 #include <sys/sysctl.h>
40 #include <sys/systm.h>
41 #include <sys/module.h>
42 #include <sys/malloc.h>
43 #include <sys/conf.h>
44 #include <sys/vnode.h>
45 #include <sys/queue.h>
46 #include <sys/device.h>
47 #include <machine/stdarg.h>
48
49 #include <sys/sysref2.h>
50
51 #include <vfs/devfs/devfs.h>
52
53 MALLOC_DEFINE(M_DEVT, "cdev_t", "dev_t storage");
54
55 static int free_devt;
56 SYSCTL_INT(_debug, OID_AUTO, free_devt, CTLFLAG_RW, &free_devt, 0, "");
57 int dev_ref_debug = 0;
58 SYSCTL_INT(_debug, OID_AUTO, dev_refs, CTLFLAG_RW, &dev_ref_debug, 0, "");
59
60 /*
61  * cdev_t and u_dev_t primitives.  Note that the major number is always
62  * extracted from si_umajor, not from si_devsw, because si_devsw is replaced
63  * when a device is destroyed.
64  */
65 int
66 major(cdev_t dev)
67 {
68         if (dev == NULL)
69                 return NOUDEV;
70         return(dev->si_umajor);
71 }
72
73 int
74 minor(cdev_t dev)
75 {
76         if (dev == NULL)
77                 return NOUDEV;
78         return(dev->si_uminor);
79 }
80
81 /*
82  * Compatibility function with old udev_t format to convert the
83  * non-consecutive minor space into a consecutive minor space.
84  */
85 int
86 lminor(cdev_t dev)
87 {
88         int y;
89
90         if (dev == NULL)
91                 return NOUDEV;
92         y = dev->si_uminor;
93         if (y & 0x0000ff00)
94                 return NOUDEV;
95         return ((y & 0xff) | (y >> 8));
96 }
97
98 /*
99  * Convert a device pointer to an old style device number.  Return NOUDEV
100  * if the device is invalid or if the device (maj,min) cannot be converted
101  * to an old style udev_t.
102  */
103 udev_t
104 dev2udev(cdev_t dev)
105 {
106         if (dev == NULL)
107                 return NOUDEV;
108
109         return (udev_t)dev->si_inode;
110 }
111
112 /*
113  * Convert a device number to a device pointer.  The device is referenced
114  * ad-hoc, meaning that the caller should call reference_dev() if it wishes
115  * to keep ahold of the returned structure long term.
116  *
117  * The returned device is associated with the currently installed cdevsw
118  * for the requested major number.  NULL is returned if the major number
119  * has not been registered.
120  */
121 cdev_t
122 udev2dev(udev_t x, int b)
123 {
124         if (x == NOUDEV || b != 0)
125                 return(NULL);
126
127         return devfs_find_device_by_udev(x);
128 }
129
130 int
131 dev_is_good(cdev_t dev)
132 {
133         if (dev != NULL && dev->si_ops != &dead_dev_ops)
134                 return(1);
135         return(0);
136 }
137
138 /*
139  * Various user device number extraction and conversion routines
140  */
141 int
142 uminor(udev_t dev)
143 {
144         if (dev == NOUDEV)
145                 return(-1);
146         return(dev & 0xffff00ff);
147 }
148
149 int
150 umajor(udev_t dev)
151 {
152         if (dev == NOUDEV)
153                 return(-1);
154         return((dev & 0xff00) >> 8);
155 }
156
157 udev_t
158 makeudev(int x, int y)
159 {
160         if ((x & 0xffffff00) || (y & 0x0000ff00))
161                 return NOUDEV;
162         return ((x << 8) | y);
163 }
164
165 /*
166  * Create an internal or external device.
167  *
168  * This routine creates and returns an unreferenced ad-hoc entry for the
169  * device which will remain intact until the device is destroyed.  If the
170  * caller intends to store the device pointer it must call reference_dev()
171  * to retain a real reference to the device.
172  *
173  * If an entry already exists, this function will set (or override)
174  * its cred requirements and name (XXX DEVFS interface).
175  */
176 cdev_t
177 make_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
178         int perms, const char *fmt, ...)
179 {
180         cdev_t  devfs_dev;
181         __va_list ap;
182         int i;
183         char dev_name[PATH_MAX+1];
184
185         /*
186          * compile the cdevsw and install the device
187          */
188         compile_dev_ops(ops);
189
190         /*
191          * Set additional fields (XXX DEVFS interface goes here)
192          */
193         __va_start(ap, fmt);
194         i = kvcprintf(fmt, NULL, dev_name, 32, ap);
195         dev_name[i] = '\0';
196         __va_end(ap);
197
198 /*
199         if ((devfs_dev = devfs_find_device_by_name(dev_name)) != NULL) {
200                 kprintf("make_dev: Device %s already exists, returning old dev without creating new node\n", dev_name);
201                 return devfs_dev;
202         }
203 */
204
205         devfs_dev = devfs_new_cdev(ops, minor);
206         memcpy(devfs_dev->si_name, dev_name, i+1);
207
208         devfs_debug(DEVFS_DEBUG_INFO,
209                     "make_dev called for %s\n",
210                     devfs_dev->si_name);
211         devfs_create_dev(devfs_dev, uid, gid, perms);
212
213         return (devfs_dev);
214 }
215
216
217 cdev_t
218 make_only_devfs_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
219         int perms, const char *fmt, ...)
220 {
221         cdev_t  devfs_dev;
222         __va_list ap;
223         int i;
224         //char *dev_name;
225
226         /*
227          * compile the cdevsw and install the device
228          */
229         compile_dev_ops(ops);
230         devfs_dev = devfs_new_cdev(ops, minor);
231
232         /*
233          * Set additional fields (XXX DEVFS interface goes here)
234          */
235         __va_start(ap, fmt);
236         i = kvcprintf(fmt, NULL, devfs_dev->si_name, 32, ap);
237         devfs_dev->si_name[i] = '\0';
238         __va_end(ap);
239
240
241         devfs_create_dev(devfs_dev, uid, gid, perms);
242
243         return (devfs_dev);
244 }
245
246
247 cdev_t
248 make_only_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
249         int perms, const char *fmt, ...)
250 {
251         cdev_t  devfs_dev;
252         __va_list ap;
253         int i;
254         //char *dev_name;
255
256         /*
257          * compile the cdevsw and install the device
258          */
259         compile_dev_ops(ops);
260         devfs_dev = devfs_new_cdev(ops, minor);
261         devfs_dev->si_perms = perms;
262         devfs_dev->si_uid = uid;
263         devfs_dev->si_gid = gid;
264
265         /*
266          * Set additional fields (XXX DEVFS interface goes here)
267          */
268         __va_start(ap, fmt);
269         i = kvcprintf(fmt, NULL, devfs_dev->si_name, 32, ap);
270         devfs_dev->si_name[i] = '\0';
271         __va_end(ap);
272
273         reference_dev(devfs_dev);
274
275         return (devfs_dev);
276 }
277
278 void
279 destroy_only_dev(cdev_t dev)
280 {
281         release_dev(dev);
282         release_dev(dev);
283         release_dev(dev);
284 }
285
286 /*
287  * destroy_dev() removes the adhoc association for a device and revectors
288  * its ops to &dead_dev_ops.
289  *
290  * This routine releases the reference count associated with the ADHOC
291  * entry, plus releases the reference count held by the caller.  What this
292  * means is that you should not call destroy_dev(make_dev(...)), because
293  * make_dev() does not bump the reference count (beyond what it needs to
294  * create the ad-hoc association).  Any procedure that intends to destroy
295  * a device must have its own reference to it first.
296  */
297 void
298 destroy_dev(cdev_t dev)
299 {
300         if (dev) {
301                 devfs_debug(DEVFS_DEBUG_DEBUG,
302                             "destroy_dev called for %s\n",
303                             dev->si_name);
304                 devfs_destroy_dev(dev);
305         }
306 }
307
308 int
309 make_dev_alias(cdev_t target, const char *fmt, ...)
310 {
311         char name[PATH_MAX + 1];
312         __va_list ap;
313         int i;
314
315         __va_start(ap, fmt);
316         i = kvcprintf(fmt, NULL, name, 32, ap);
317         name[i] = '\0';
318         __va_end(ap);
319
320         devfs_make_alias(name, target);
321
322         return 0;
323 }
324
325 extern struct dev_ops default_dev_ops;
326
327 cdev_t
328 make_autoclone_dev(struct dev_ops *ops, struct devfs_bitmap *bitmap,
329                 d_clone_t *nhandler, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
330 {
331         cdev_t dev;
332         char name[PATH_MAX + 1];
333         __va_list ap;
334         int i;
335
336         __va_start(ap, fmt);
337         i = kvcprintf(fmt, NULL, name, 32, ap);
338         name[i] = '\0';
339         __va_end(ap);
340
341         if (bitmap != NULL)
342                 devfs_clone_bitmap_init(bitmap);
343
344         devfs_clone_handler_add(name, nhandler);
345         dev = make_dev(&default_dev_ops, 0xffff00ff, uid, gid, perms, name);
346
347         return dev;
348 }
349
350 void
351 destroy_autoclone_dev(cdev_t dev, struct devfs_bitmap *bitmap)
352 {
353         if (dev == NULL)
354                 return;
355
356         devfs_clone_handler_del(dev->si_name);
357
358         if (bitmap != NULL)
359                 devfs_clone_bitmap_uninit(bitmap);
360
361         destroy_dev(dev);
362 }
363
364
365 /*
366  * Add a reference to a device.  Callers generally add their own references
367  * when they are going to store a device node in a variable for long periods
368  * of time, to prevent a disassociation from free()ing the node.
369  *
370  * Also note that a caller that intends to call destroy_dev() must first
371  * obtain a reference on the device.  The ad-hoc reference you get with
372  * make_dev() and friends is NOT sufficient to be able to call destroy_dev().
373  */
374 cdev_t
375 reference_dev(cdev_t dev)
376 {
377         //kprintf("reference_dev\n");
378
379         if (dev != NULL) {
380                 sysref_get(&dev->si_sysref);
381                 if (dev_ref_debug & 2) {
382                         kprintf("reference dev %p %s(minor=%08x) refs=%d\n",
383                             dev, devtoname(dev), dev->si_uminor,
384                             dev->si_sysref.refcnt);
385                 }
386         }
387         return(dev);
388 }
389
390 /*
391  * release a reference on a device.  The device will be terminated when the
392  * last reference has been released.
393  *
394  * NOTE: we must use si_umajor to figure out the original major number,
395  * because si_ops could already be pointing at dead_dev_ops.
396  */
397 void
398 release_dev(cdev_t dev)
399 {
400         //kprintf("release_dev\n");
401
402         if (dev == NULL)
403                 return;
404         sysref_put(&dev->si_sysref);
405 }
406
407 const char *
408 devtoname(cdev_t dev)
409 {
410         int mynor;
411         int len;
412         char *p;
413         const char *dname;
414
415         if (dev == NULL)
416                 return("#nodev");
417         if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
418                 p = dev->si_name;
419                 len = sizeof(dev->si_name);
420                 if ((dname = dev_dname(dev)) != NULL)
421                         ksnprintf(p, len, "#%s/", dname);
422                 else
423                         ksnprintf(p, len, "#%d/", major(dev));
424                 len -= strlen(p);
425                 p += strlen(p);
426                 mynor = minor(dev);
427                 if (mynor < 0 || mynor > 255)
428                         ksnprintf(p, len, "%#x", (u_int)mynor);
429                 else
430                         ksnprintf(p, len, "%d", mynor);
431         }
432         return (dev->si_name);
433 }
434