#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/vnode.h>
#include <sys/queue.h>
#include <sys/device.h>
#include <sys/disk.h>
#include <machine/stdarg.h>
#include <sys/sysref2.h>
#include <sys/devfs.h>
static int dev_ref_debug = 0;
SYSCTL_INT(_debug, OID_AUTO, dev_refs, CTLFLAG_RW, &dev_ref_debug, 0,
"Toggle device reference debug output");
int
major(cdev_t dev)
{
if (dev == NULL)
return NOUDEV;
return(dev->si_umajor);
}
int
minor(cdev_t dev)
{
if (dev == NULL)
return NOUDEV;
return(dev->si_uminor);
}
int
lminor(cdev_t dev)
{
int y;
if (dev == NULL)
return NOUDEV;
y = dev->si_uminor;
if (y & 0x0000ff00)
return NOUDEV;
return ((y & 0xff) | (y >> 8));
}
dev_t
devid_from_dev(cdev_t dev)
{
if (dev == NULL)
return NOUDEV;
return (dev_t)dev->si_inode;
}
cdev_t
dev_from_devid(dev_t x, int b)
{
if (x == NOUDEV || b != 0)
return(NULL);
return devfs_find_device_by_devid(x);
}
int
dev_is_good(cdev_t dev)
{
if (dev != NULL && dev->si_ops != &dead_dev_ops)
return(1);
return(0);
}
int
uminor(dev_t dev)
{
if (dev == NOUDEV)
return(-1);
return(dev & 0xffff00ff);
}
int
umajor(dev_t dev)
{
if (dev == NOUDEV)
return(-1);
return((dev & 0xff00) >> 8);
}
dev_t
makeudev(int x, int y)
{
if ((x & 0xffffff00) || (y & 0x0000ff00))
return NOUDEV;
return ((x << 8) | y);
}
char *
makedev_unit_b32(char *nbuf, uintmax_t num)
{
char *p = &nbuf[MAKEDEV_MINNBUF - 1];
*p = '\0';
do {
*--p = hex2ascii(num % 32);
} while (num /= 32);
return (p);
}
cdev_t
make_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
int perms, const char *fmt, ...)
{
cdev_t devfs_dev;
__va_list ap;
compile_dev_ops(ops);
devfs_dev = devfs_new_cdev(ops, minor, NULL);
__va_start(ap, fmt);
kvsnprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), fmt, ap);
__va_end(ap);
devfs_debug(DEVFS_DEBUG_INFO,
"make_dev called for %s\n",
devfs_dev->si_name);
devfs_create_dev(devfs_dev, uid, gid, perms);
return (devfs_dev);
}
cdev_t
make_dev_covering(struct dev_ops *ops, struct dev_ops *bops, int minor,
uid_t uid, gid_t gid, int perms, const char *fmt, ...)
{
cdev_t devfs_dev;
__va_list ap;
compile_dev_ops(ops);
devfs_dev = devfs_new_cdev(ops, minor, bops);
__va_start(ap, fmt);
kvsnprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), fmt, ap);
__va_end(ap);
devfs_debug(DEVFS_DEBUG_INFO,
"make_dev called for %s\n",
devfs_dev->si_name);
devfs_create_dev(devfs_dev, uid, gid, perms);
return (devfs_dev);
}
cdev_t
make_only_devfs_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
int perms, const char *fmt, ...)
{
cdev_t devfs_dev;
__va_list ap;
compile_dev_ops(ops);
devfs_dev = devfs_new_cdev(ops, minor, NULL);
__va_start(ap, fmt);
kvsnprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), fmt, ap);
__va_end(ap);
devfs_create_dev(devfs_dev, uid, gid, perms);
return (devfs_dev);
}
cdev_t
make_only_dev(struct dev_ops *ops, int minor, uid_t uid, gid_t gid,
int perms, const char *fmt, ...)
{
cdev_t devfs_dev;
__va_list ap;
compile_dev_ops(ops);
devfs_dev = devfs_new_cdev(ops, minor, NULL);
devfs_dev->si_perms = perms;
devfs_dev->si_uid = uid;
devfs_dev->si_gid = gid;
__va_start(ap, fmt);
kvsnprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), fmt, ap);
__va_end(ap);
reference_dev(devfs_dev);
return (devfs_dev);
}
cdev_t
make_only_dev_covering(struct dev_ops *ops, struct dev_ops *bops, int minor,
uid_t uid, gid_t gid, int perms, const char *fmt, ...)
{
cdev_t devfs_dev;
__va_list ap;
compile_dev_ops(ops);
devfs_dev = devfs_new_cdev(ops, minor, bops);
devfs_dev->si_perms = perms;
devfs_dev->si_uid = uid;
devfs_dev->si_gid = gid;
__va_start(ap, fmt);
kvsnprintf(devfs_dev->si_name, sizeof(devfs_dev->si_name), fmt, ap);
__va_end(ap);
reference_dev(devfs_dev);
return (devfs_dev);
}
void
destroy_only_dev(cdev_t dev)
{
release_dev(dev);
release_dev(dev);
release_dev(dev);
}
void
destroy_dev(cdev_t dev)
{
if (dev) {
devfs_debug(DEVFS_DEBUG_DEBUG,
"destroy_dev called for %s\n",
dev->si_name);
devfs_destroy_dev(dev);
}
}
void
sync_devs(void)
{
disk_config(NULL);
devfs_config();
disk_config(NULL);
devfs_config();
}
int
make_dev_alias(cdev_t target, const char *fmt, ...)
{
__va_list ap;
char *name;
__va_start(ap, fmt);
kvasnprintf(&name, PATH_MAX, fmt, ap);
__va_end(ap);
devfs_make_alias(name, target);
kvasfree(&name);
return 0;
}
int
destroy_dev_alias(cdev_t target, const char *fmt, ...)
{
__va_list ap;
char *name;
__va_start(ap, fmt);
kvasnprintf(&name, PATH_MAX, fmt, ap);
__va_end(ap);
devfs_destroy_alias(name, target);
kvasfree(&name);
return 0;
}
extern struct dev_ops default_dev_ops;
cdev_t
make_autoclone_dev(struct dev_ops *ops, struct devfs_bitmap *bitmap,
d_clone_t *nhandler, uid_t uid, gid_t gid, int perms, const char *fmt, ...)
{
__va_list ap;
cdev_t dev;
char *name;
__va_start(ap, fmt);
kvasnprintf(&name, PATH_MAX, fmt, ap);
__va_end(ap);
if (bitmap != NULL)
devfs_clone_bitmap_init(bitmap);
devfs_clone_handler_add(name, nhandler);
dev = make_dev_covering(&default_dev_ops, ops, 0xffff00ff,
uid, gid, perms, "%s", name);
kvasfree(&name);
return dev;
}
void
destroy_autoclone_dev(cdev_t dev, struct devfs_bitmap *bitmap)
{
if (dev == NULL)
return;
devfs_clone_handler_del(dev->si_name);
if (bitmap != NULL)
devfs_clone_bitmap_uninit(bitmap);
destroy_dev(dev);
}
cdev_t
reference_dev(cdev_t dev)
{
if (dev != NULL) {
sysref_get(&dev->si_sysref);
if (dev_ref_debug & 2) {
kprintf("reference dev %p %s(minor=%08x) refs=%d\n",
dev, devtoname(dev), dev->si_uminor,
dev->si_sysref.refcnt);
}
}
return(dev);
}
void
release_dev(cdev_t dev)
{
if (dev == NULL)
return;
sysref_put(&dev->si_sysref);
}
const char *
devtoname(cdev_t dev)
{
int mynor;
int len;
char *p;
const char *dname;
if (dev == NULL)
return("#nodev");
if (dev->si_name[0] == '#' || dev->si_name[0] == '\0') {
p = dev->si_name;
len = sizeof(dev->si_name);
if ((dname = dev_dname(dev)) != NULL)
ksnprintf(p, len, "#%s/", dname);
else
ksnprintf(p, len, "#%d/", major(dev));
len -= strlen(p);
p += strlen(p);
mynor = minor(dev);
if (mynor < 0 || mynor > 255)
ksnprintf(p, len, "%#x", (u_int)mynor);
else
ksnprintf(p, len, "%d", mynor);
}
return (dev->si_name);
}