#include <sys/param.h>
#include <sys/modctl.h>
#include <sys/sysmacros.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/pathname.h>
#include <sys/ddi_impldefs.h>
#include <sys/sunddi.h>
#include <sys/autoconf.h>
#include <sys/modhash.h>
#include <sys/dacf.h>
#include <sys/dacf_impl.h>
#include <sys/systm.h>
#include <sys/varargs.h>
#include <sys/debug.h>
#include <sys/log.h>
#include <sys/fs/snode.h>
typedef struct dacf_opmap {
const char *name;
dacf_opid_t id;
} dacf_opmap_t;
static dacf_opmap_t dacf_ops[] = {
{ "post-attach", DACF_OPID_POSTATTACH },
{ "pre-detach", DACF_OPID_PREDETACH },
{ NULL, 0 },
};
typedef struct dacf_opt {
const char *optname;
uint_t optmask;
} dacf_opt_t;
static dacf_opt_t dacf_options[] = {
#ifdef DEBUG
{ "testopt", 1 },
{ "testopt2", 2 },
#endif
{ NULL, 0 },
};
static char kmod_name[] = "__kernel";
typedef struct dacf_ds {
const char *name;
dacf_devspec_t id;
} dacf_ds_t;
static dacf_ds_t dacf_devspecs[] = {
{ "minor-nodetype", DACF_DS_MIN_NT },
{ "driver-minorname", DACF_DS_DRV_MNAME },
{ "device-path", DACF_DS_DEV_PATH },
{ NULL, DACF_DS_ERROR },
};
mod_hash_t *posta_mntype, *posta_mname, *posta_devname;
mod_hash_t *pred_mntype, *pred_mname, *pred_devname;
mod_hash_t *dacf_module_hash;
mod_hash_t *dacf_info_hash;
mod_hash_t **dacf_rule_matrix[DACF_NUM_OPIDS][DACF_NUM_DEVSPECS] = {
{ &posta_mntype, &posta_mname, &posta_devname },
{ &pred_mntype, &pred_mname, &pred_devname },
};
kmutex_t dacf_lock;
kmutex_t dacf_module_lock;
int dacfdebug = 0;
static dacf_rule_t *dacf_rule_ctor(char *, char *, char *, dacf_opid_t,
uint_t, dacf_arg_t *);
static mod_hash_t *dacf_get_op_hash(dacf_opid_t, dacf_devspec_t);
static void dacf_rule_val_dtor(mod_hash_val_t);
static void dacf_destroy_opsets(dacf_module_t *module);
static void dacf_opset_copy(dacf_opset_t *dst, dacf_opset_t *src);
static void dprintf(const char *, ...) __KPRINTFLIKE(1);
static void
dprintf(const char *format, ...)
{
va_list alist;
char dp_buf[256], *dpbp;
if (dacfdebug & DACF_DBG_MSGS) {
va_start(alist, format);
(void) sprintf(dp_buf, "dacf debug: ");
dpbp = &(dp_buf[strlen(dp_buf)]);
(void) vsnprintf(dpbp, sizeof (dp_buf) - strlen(dp_buf),
format, alist);
printf(dp_buf);
va_end(alist);
}
}
void
dacf_init()
{
int i, j;
char hbuf[40];
mutex_enter(&dacf_lock);
dprintf("dacf_init: creating hashmatrix\n");
#ifdef DEBUG
for (i = 0; dacf_devspecs[i].name != NULL; i++)
continue;
ASSERT(i == DACF_NUM_DEVSPECS);
for (i = 0; dacf_ops[i].name != NULL; i++)
continue;
ASSERT(i == DACF_NUM_OPIDS);
#endif
for (i = 0; i < DACF_NUM_OPIDS; i++) {
for (j = 0; j < DACF_NUM_DEVSPECS; j++) {
if (dacf_rule_matrix[i][j] == NULL) {
continue;
}
(void) snprintf(hbuf, sizeof (hbuf),
"dacf hashmatrix [%d][%d]", i, j);
*(dacf_rule_matrix[i][j]) = mod_hash_create_extended(
hbuf,
DACF_RULE_HASHSIZE,
mod_hash_null_keydtor,
dacf_rule_val_dtor,
mod_hash_bystr, NULL,
mod_hash_strkey_cmp,
KM_SLEEP);
}
}
dprintf("dacf_init: creating module_hash\n");
dacf_module_hash = mod_hash_create_strhash("dacf module hash",
DACF_MODULE_HASHSIZE, mod_hash_null_valdtor);
dprintf("dacf_init: creating info_hash\n");
dacf_info_hash = mod_hash_create_ptrhash("dacf info hash",
DACF_INFO_HASHSIZE, mod_hash_null_valdtor,
sizeof (struct ddi_minor_data));
mutex_exit(&dacf_lock);
(void) dacf_module_register(kmod_name, &kmod_dacfsw);
(void) read_dacf_binding_file(NULL);
dprintf("dacf_init: dacf is ready\n");
}
void
dacf_clear_rules()
{
int i, j;
ASSERT(MUTEX_HELD(&dacf_lock));
for (i = 0; i < DACF_NUM_OPIDS; i++) {
for (j = 0; j < DACF_NUM_DEVSPECS; j++) {
if ((dacf_rule_matrix[i][j] != NULL) &&
(*(dacf_rule_matrix[i][j]) != NULL)) {
mod_hash_clear(*(dacf_rule_matrix[i][j]));
}
}
}
}
int
dacf_rule_insert(dacf_devspec_t devspec_type, char *devspec_data,
char *module, char *opset, dacf_opid_t opid, uint_t opts,
dacf_arg_t *op_args)
{
dacf_rule_t *rule;
mod_hash_t *hash;
ASSERT(devspec_type != DACF_DS_ERROR);
ASSERT(devspec_data);
ASSERT(opset);
ASSERT(MUTEX_HELD(&dacf_lock));
dprintf("dacf_rule_insert called: %s=\"%s\", %s:%s, %s\n",
dacf_devspec_to_str(devspec_type), devspec_data,
module ? module : "[kernel]", opset, dacf_opid_to_str(opid));
hash = dacf_get_op_hash(opid, devspec_type);
if (hash == NULL) {
cmn_err(CE_WARN, "!dacf dev-spec '%s' does not support op '%s'",
dacf_devspec_to_str(devspec_type), dacf_opid_to_str(opid));
return (-1);
}
rule = dacf_rule_ctor(devspec_data, module, opset, opid, opts,
op_args);
dacf_rule_hold(rule);
if (mod_hash_insert(hash, (mod_hash_key_t)rule->r_devspec_data,
(mod_hash_val_t)rule) != 0) {
dacf_rule_rele(rule);
cmn_err(CE_WARN, "!dacf rule %s='%s' %s:%s %s duplicates "
"another rule, ignored", dacf_devspec_to_str(devspec_type),
devspec_data, module, opset, dacf_opid_to_str(opid));
return (-1);
}
return (0);
}
static dacf_rule_t *
dacf_rule_ctor(char *device_spec, char *module, char *opset, dacf_opid_t opid,
uint_t opts, dacf_arg_t *op_args)
{
dacf_rule_t *rule;
dacf_arg_t *p;
rule = kmem_alloc(sizeof (dacf_rule_t), KM_SLEEP);
rule->r_devspec_data = kmem_alloc(strlen(device_spec) + 1, KM_SLEEP);
(void) strcpy(rule->r_devspec_data, device_spec);
if (module == NULL) {
module = kmod_name;
}
rule->r_module = kmem_alloc(strlen(module) + 1, KM_SLEEP);
(void) strcpy(rule->r_module, module);
rule->r_opset = kmem_alloc(strlen(opset) + 1, KM_SLEEP);
(void) strcpy(rule->r_opset, opset);
rule->r_refs = 0;
rule->r_opts = opts;
rule->r_opid = opid;
rule->r_args = NULL;
p = op_args;
while (p != NULL) {
ASSERT(p->arg_name);
ASSERT(p->arg_val);
(void) dacf_arg_insert(&rule->r_args, p->arg_name, p->arg_val);
p = p->arg_next;
}
return (rule);
}
static void
dacf_rule_val_dtor(mod_hash_val_t val)
{
ASSERT((void *)val != NULL);
dacf_rule_rele((dacf_rule_t *)val);
}
void
dacf_rule_destroy(dacf_rule_t *rule)
{
ASSERT(rule->r_refs == 0);
dacf_arglist_delete(&(rule->r_args));
kmem_free(rule->r_devspec_data, strlen(rule->r_devspec_data) + 1);
kmem_free(rule->r_module, strlen(rule->r_module) + 1);
kmem_free(rule->r_opset, strlen(rule->r_opset) + 1);
kmem_free(rule, sizeof (dacf_rule_t));
}
void
dacf_rule_hold(dacf_rule_t *rule)
{
ASSERT(MUTEX_HELD(&dacf_lock));
rule->r_refs++;
}
void
dacf_rule_rele(dacf_rule_t *rule)
{
ASSERT(MUTEX_HELD(&dacf_lock));
ASSERT(rule->r_refs > 0);
rule->r_refs--;
if (rule->r_refs == 0) {
dacf_rule_destroy(rule);
}
}
void
dacf_rsrv_make(dacf_rsrvlist_t *rsrv, dacf_rule_t *rule, void *info,
dacf_rsrvlist_t **list)
{
dacf_infohdl_t ihdl = info;
ASSERT(MUTEX_HELD(&dacf_lock));
ASSERT(info && rule && list);
dacf_rule_hold(rule);
rsrv->rsrv_rule = rule;
rsrv->rsrv_ihdl = ihdl;
rsrv->rsrv_result = DDI_SUCCESS;
rsrv->rsrv_next = *list;
*list = rsrv;
dprintf("dacf: reservation made\n");
}
void
dacf_clr_rsrvs(dev_info_t *devi, dacf_opid_t op)
{
dacf_process_rsrvs(&(DEVI(devi)->devi_dacf_tasks), op, DACF_PROC_RELE);
}
void
dacf_process_rsrvs(dacf_rsrvlist_t **list, dacf_opid_t op, int flags)
{
dacf_rsrvlist_t *p, *dp;
dacf_rsrvlist_t **prevptr;
ASSERT(MUTEX_HELD(&dacf_lock));
ASSERT(list);
ASSERT(flags != 0);
if (*list == NULL)
return;
dprintf("dacf_process_rsrvs: opid = %d, flags = 0x%x\n", op, flags);
prevptr = list;
for (p = *list; p != NULL; ) {
if (p->rsrv_rule->r_opid != op) {
prevptr = &(p->rsrv_next);
p = p->rsrv_next;
continue;
}
if (flags & DACF_PROC_INVOKE) {
p->rsrv_result = dacf_op_invoke(p->rsrv_rule,
p->rsrv_ihdl, 0);
}
if (flags & DACF_PROC_RELE) {
*prevptr = p->rsrv_next;
dp = p;
p = p->rsrv_next;
dacf_rule_rele(dp->rsrv_rule);
kmem_free(dp, sizeof (dacf_rsrvlist_t));
} else {
prevptr = &(p->rsrv_next);
p = p->rsrv_next;
}
}
}
static mod_hash_t *
dacf_get_op_hash(dacf_opid_t op, dacf_devspec_t ds_type)
{
ASSERT(op <= DACF_NUM_OPIDS && op > 0);
ASSERT(ds_type <= DACF_NUM_DEVSPECS && ds_type > 0);
if (dacf_rule_matrix[op - 1][ds_type - 1] == NULL) {
return (NULL);
}
return (*(dacf_rule_matrix[op - 1][ds_type - 1]));
}
int
dacf_arg_insert(dacf_arg_t **list, char *name, char *val)
{
dacf_arg_t *arg;
for (arg = *list; arg != NULL; arg = arg->arg_next) {
if (strcmp(arg->arg_name, name) == 0) {
return (-1);
}
}
arg = kmem_alloc(sizeof (dacf_arg_t), KM_SLEEP);
arg->arg_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
(void) strcpy(arg->arg_name, name);
arg->arg_val = kmem_alloc(strlen(val) + 1, KM_SLEEP);
(void) strcpy(arg->arg_val, val);
arg->arg_next = *list;
*list = arg;
return (0);
}
void
dacf_arglist_delete(dacf_arg_t **list)
{
dacf_arg_t *arg, *narg;
arg = *list;
while (arg != NULL) {
narg = arg->arg_next;
kmem_free(arg->arg_name, strlen(arg->arg_name) + 1);
kmem_free(arg->arg_val, strlen(arg->arg_val) + 1);
kmem_free(arg, sizeof (dacf_arg_t));
arg = narg;
}
*list = NULL;
}
dacf_rule_t *
dacf_match(dacf_opid_t op, dacf_devspec_t ds, const void *match_info)
{
dacf_rule_t *rule;
ASSERT(MUTEX_HELD(&dacf_lock));
if (mod_hash_find(dacf_get_op_hash(op, ds), (mod_hash_key_t)match_info,
(mod_hash_val_t *)&rule) == 0) {
return (rule);
}
return (NULL);
}
int
dacf_module_register(char *mod_name, struct dacfsw *sw)
{
char *str;
size_t i, nelems;
dacf_module_t *module;
dacf_opset_t *opsarray;
if (sw == NULL) {
return (EINVAL);
}
if (sw->dacf_rev != DACF_MODREV_1) {
cmn_err(CE_WARN, "dacf: module '%s' exports unsupported "
"version %d interface, not registered\n", mod_name,
sw->dacf_rev);
return (EINVAL);
}
for (nelems = 0; sw->dacf_opsets[nelems].opset_name != NULL; nelems++)
;
dprintf("dacf_module_register: found %lu opsets\n", nelems);
if ((nelems == 0) && (sw != &kmod_dacfsw)) {
cmn_err(CE_WARN, "dacf module %s exports no opsets, "
"not registered.\n", mod_name);
return (EINVAL);
}
if (mod_hash_find(dacf_module_hash, (mod_hash_key_t)mod_name,
(mod_hash_val_t)&module) == 0) {
rw_enter(&module->dm_lock, RW_WRITER);
if (module->dm_loaded) {
rw_exit(&module->dm_lock);
cmn_err(CE_WARN, "dacf module '%s' is "
"already registered.", mod_name);
return (EBUSY);
}
} else {
module = kmem_zalloc(sizeof (dacf_module_t), KM_SLEEP);
str = kmem_alloc(strlen(mod_name) + 1, KM_SLEEP);
(void) strcpy(str, mod_name);
rw_enter(&module->dm_lock, RW_WRITER);
if (mod_hash_insert(dacf_module_hash, (mod_hash_key_t)str,
(mod_hash_val_t)module) != 0) {
rw_exit(&module->dm_lock);
kmem_free(str, strlen(str) + 1);
kmem_free(module, sizeof (dacf_module_t));
cmn_err(CE_WARN, "dacf module '%s' is "
"already registered.", mod_name);
return (EBUSY);
}
}
ASSERT(RW_WRITE_HELD(&module->dm_lock));
opsarray = kmem_zalloc(sizeof (dacf_opset_t) * (nelems + 1), KM_SLEEP);
for (i = 0; i < nelems; i++) {
dacf_opset_copy(&(opsarray[i]), &(sw->dacf_opsets[i]));
ASSERT(opsarray[i].opset_name != NULL);
ASSERT(opsarray[i].opset_ops != NULL);
}
opsarray[nelems].opset_name = NULL;
opsarray[nelems].opset_ops = NULL;
ASSERT(module->dm_opsets == NULL);
module->dm_opsets = opsarray;
if (dacfdebug & DACF_DBG_MSGS) {
dprintf("%s registered.\n", mod_name);
for (i = 0; i < nelems; i++) {
dprintf("registered %s\n", opsarray[i].opset_name);
}
}
module->dm_loaded = 1;
rw_exit(&module->dm_lock);
return (0);
}
int
dacf_module_unregister(char *mod_name)
{
dacf_module_t *module;
ASSERT(strcmp(mod_name, kmod_name) != 0);
dprintf("dacf_module_unregister: called for '%s'!\n", mod_name);
if (mod_hash_find(dacf_module_hash, (mod_hash_key_t)mod_name,
(mod_hash_val_t)&module) == 0) {
if ((moddebug & MODDEBUG_NOAUL_DACF) ||
!rw_tryenter(&module->dm_lock, RW_WRITER)) {
return (EBUSY);
}
} else {
return (EINVAL);
}
ASSERT(RW_WRITE_HELD(&module->dm_lock));
dacf_destroy_opsets(module);
module->dm_loaded = 0;
rw_exit(&module->dm_lock);
return (0);
}
static void
dacf_destroy_opsets(dacf_module_t *module)
{
dacf_opset_t *array = module->dm_opsets;
dacf_opset_t *p;
int i;
size_t nelems;
ASSERT(RW_WRITE_HELD(&module->dm_lock));
ASSERT(module->dm_loaded == 1);
for (i = 0; array[i].opset_name != NULL; i++) {
p = &(array[i]);
kmem_free(p->opset_name, strlen(p->opset_name) + 1);
for (nelems = 0; ; nelems++) {
if (p->opset_ops[nelems].op_id == DACF_OPID_END) {
break;
}
}
kmem_free(p->opset_ops, sizeof (dacf_op_t) * (nelems + 1));
}
kmem_free(array, (sizeof (dacf_opset_t)) * (i + 1));
module->dm_opsets = NULL;
}
static void
dacf_opset_copy(dacf_opset_t *dst, dacf_opset_t *src)
{
size_t nelems, i;
ASSERT(src && dst);
dprintf("dacf_opset_copy: called\n");
dst->opset_name = kmem_alloc(strlen(src->opset_name) + 1, KM_SLEEP);
(void) strcpy(dst->opset_name, src->opset_name);
dprintf("dacf_opset_copy: counting ops\n");
for (nelems = 0; ; nelems++) {
if ((src->opset_ops[nelems].op_id == DACF_OPID_END) ||
(src->opset_ops[nelems].op_func == NULL)) {
break;
}
}
dprintf("dacf_opset_copy: found %lu ops\n", nelems);
dst->opset_ops = kmem_alloc(sizeof (dacf_op_t) * (nelems + 1),
KM_SLEEP);
dprintf("dacf_opset_copy: copying ops\n");
for (i = 0; i < nelems; i++) {
dst->opset_ops[i].op_id = src->opset_ops[i].op_id;
dst->opset_ops[i].op_func = src->opset_ops[i].op_func;
}
dst->opset_ops[nelems].op_id = DACF_OPID_END;
dst->opset_ops[nelems].op_func = NULL;
dprintf("dacf_opset_copy: done copying ops\n");
}
int dacf_modload_laps = 0;
int
dacf_op_invoke(dacf_rule_t *rule, dacf_infohdl_t info_hdl, int flags)
{
dacf_module_t *module;
dacf_opset_t *opsarray;
dacf_opset_t *opset;
dacf_op_t *op = NULL;
dacf_opid_t op_id;
dacf_arghdl_t arg_hdl;
dev_info_t *dip;
int i, rval = -1;
ASSERT(rule);
ASSERT(MUTEX_HELD(&dacf_lock));
op_id = rule->r_opid;
dprintf("dacf_op_invoke: opid=%d\n", op_id);
for (;;) {
if (mod_hash_find(dacf_module_hash,
(mod_hash_key_t)rule->r_module,
(mod_hash_val_t *)&module) == 0) {
rw_enter(&module->dm_lock, RW_READER);
if (module->dm_loaded != 0) {
break;
}
rw_exit(&module->dm_lock);
}
dprintf("dacf_op_invoke: calling modload\n");
if (modload("dacf", rule->r_module) < 0) {
return (DACF_ERR_MOD_NOTFOUND);
}
dacf_modload_laps++;
}
ASSERT(RW_READ_HELD(&module->dm_lock));
opsarray = module->dm_opsets;
opset = NULL;
for (i = 0; opsarray[i].opset_name != NULL; i++) {
if (strcmp(opsarray[i].opset_name, rule->r_opset) == 0) {
opset = &opsarray[i];
break;
}
}
if (opset == NULL) {
cmn_err(CE_WARN, "!dacf: couldn't invoke op, opset '%s' not "
"found in module '%s'", rule->r_opset, rule->r_module);
rw_exit(&module->dm_lock);
return (DACF_ERR_OPSET_NOTFOUND);
}
arg_hdl = (dacf_arghdl_t)rule->r_args;
op = NULL;
for (i = 0; opset->opset_ops[i].op_id != DACF_OPID_END; i++) {
if (opset->opset_ops[i].op_id == op_id) {
op = &(opset->opset_ops[i]);
break;
}
}
if (op == NULL) {
cmn_err(CE_WARN, "!dacf: couldn't invoke op, op '%s' not found "
"in opset '%s' in module '%s'", dacf_opid_to_str(op_id),
rule->r_opset, rule->r_module);
rw_exit(&module->dm_lock);
return (DACF_ERR_OP_NOTFOUND);
}
dprintf("dacf_op_invoke: found op, invoking...\n");
dip = ((struct ddi_minor_data *)info_hdl)->dip;
mutex_enter(&(DEVI(dip)->devi_lock));
DEVI_SET_INVOKING_DACF(dip);
mutex_exit(&(DEVI(dip)->devi_lock));
mutex_exit(&dacf_lock);
rval = op->op_func(info_hdl, arg_hdl, flags);
mutex_enter(&dacf_lock);
mutex_enter(&(DEVI(dip)->devi_lock));
DEVI_CLR_INVOKING_DACF(dip);
mutex_exit(&(DEVI(dip)->devi_lock));
rw_exit(&module->dm_lock);
if (rval == DACF_SUCCESS) {
return (DACF_SUCCESS);
} else {
return (DACF_ERR_OP_FAILED);
}
}
dacf_devspec_t
dacf_get_devspec(char *name)
{
dacf_ds_t *p = &dacf_devspecs[0];
while (p->name != NULL) {
if (strcmp(p->name, name) == 0) {
return (p->id);
}
p++;
}
return (DACF_DS_ERROR);
}
const char *
dacf_devspec_to_str(dacf_devspec_t ds)
{
dacf_ds_t *p = &dacf_devspecs[0];
while (p->name != NULL) {
if (p->id == ds) {
return (p->name);
}
p++;
}
return (NULL);
}
dacf_opid_t
dacf_get_op(char *name)
{
dacf_opmap_t *p = &dacf_ops[0];
while (p->name != NULL) {
if (strcmp(p->name, name) == 0) {
return (p->id);
}
p++;
}
return (DACF_OPID_ERROR);
}
const char *
dacf_opid_to_str(dacf_opid_t tid)
{
dacf_opmap_t *p = &dacf_ops[0];
while (p->name != NULL) {
if (p->id == tid) {
return (p->name);
}
p++;
}
return (NULL);
}
int
dacf_getopt(char *opt_str, uint_t *opts)
{
dacf_opt_t *p = &dacf_options[0];
while (p->optname != NULL) {
if (strcmp(opt_str, p->optname) == 0) {
*opts |= p->optmask;
return (0);
}
p++;
}
return (-1);
}
const char *
dacf_minor_name(dacf_infohdl_t info_hdl)
{
struct ddi_minor_data *dmdp = (struct ddi_minor_data *)info_hdl;
return (dmdp->ddm_name);
}
minor_t
dacf_minor_number(dacf_infohdl_t info_hdl)
{
struct ddi_minor_data *dmdp = (struct ddi_minor_data *)info_hdl;
return (getminor(dmdp->ddm_dev));
}
dev_t
dacf_get_dev(dacf_infohdl_t info_hdl)
{
struct ddi_minor_data *dmdp = (struct ddi_minor_data *)info_hdl;
return (dmdp->ddm_dev);
}
const char *
dacf_driver_name(dacf_infohdl_t info_hdl)
{
struct ddi_minor_data *dmdp = (struct ddi_minor_data *)info_hdl;
return (ddi_driver_name(dmdp->dip));
}
dev_info_t *
dacf_devinfo_node(dacf_infohdl_t info_hdl)
{
struct ddi_minor_data *dmdp = (struct ddi_minor_data *)info_hdl;
return (dmdp->dip);
}
const char *
dacf_get_arg(dacf_arghdl_t arghdl, char *arg_name)
{
dacf_arg_t *arg_list = (dacf_arg_t *)arghdl;
ASSERT(arg_name);
while (arg_list != NULL) {
if (strcmp(arg_list->arg_name, arg_name) == 0) {
return (arg_list->arg_val);
}
arg_list = arg_list->arg_next;
}
return (NULL);
}
void
dacf_store_info(dacf_infohdl_t info_hdl, void *data)
{
struct ddi_minor_data *dmdp = (struct ddi_minor_data *)info_hdl;
if (data == NULL) {
(void) mod_hash_destroy(dacf_info_hash, (mod_hash_key_t)dmdp);
} else {
(void) mod_hash_replace(dacf_info_hash, (mod_hash_key_t)dmdp,
(mod_hash_val_t)data);
}
}
void *
dacf_retrieve_info(dacf_infohdl_t info_hdl)
{
struct ddi_minor_data *dmdp = (struct ddi_minor_data *)info_hdl;
void *data;
if (mod_hash_find(dacf_info_hash, (mod_hash_key_t)dmdp,
(mod_hash_val_t *)&data) != 0) {
return (NULL);
}
return (data);
}
struct vnode *
dacf_makevp(dacf_infohdl_t info_hdl)
{
struct ddi_minor_data *dmdp = (struct ddi_minor_data *)info_hdl;
struct vnode *vp;
vp = makespecvp(dmdp->ddm_dev, VCHR);
spec_assoc_vp_with_devi(vp, dmdp->dip);
return (vp);
}