#include <sys/types.h>
#include <sys/modctl.h>
#include <sys/stat.h>
#include <sys/proc.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/kmem.h>
#include <sys/file.h>
#include <sys/rsm/rsm_common.h>
#include <sys/rsm/rsmpi.h>
#include <sys/rsm/rsmpi_driver.h>
static struct modlmisc modlmisc = {
&mod_miscops, "RSMOPS module",
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
static kmutex_t rsmops_lock;
static rsmops_drv_t *rsmops_drv_head = NULL;
static int rsmops_threads_started = 0;
int
_init(void)
{
int err;
mutex_init(&rsmops_lock, NULL, MUTEX_DEFAULT, NULL);
if ((err = mod_install(&modlinkage)) != 0)
mutex_destroy(&rsmops_lock);
return (err);
}
int
_fini(void)
{
int err;
mutex_enter(&rsmops_lock);
if (rsmops_drv_head) {
mutex_exit(&rsmops_lock);
return (EBUSY);
}
if (rsmops_threads_started) {
mutex_exit(&rsmops_lock);
return (EBUSY);
}
mutex_exit(&rsmops_lock);
if ((err = mod_remove(&modlinkage)) == 0)
mutex_destroy(&rsmops_lock);
return (err);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static void
rsmops_thread_entry(rsmops_drv_t *p_drv)
{
ASSERT(p_drv->drv.rsm_thread_entry_pt);
(*(p_drv->drv.rsm_thread_entry_pt))(p_drv->drv.drv_name);
mutex_enter(&rsmops_lock);
p_drv->ctrl_cnt--;
mutex_exit(&rsmops_lock);
}
int
rsm_register_driver(rsmops_registry_t *p_registry)
{
rsmops_drv_t **pp_tail;
rsmops_drv_t *p;
if (p_registry->rsm_version > RSM_VERSION) {
return (RSMERR_BAD_DRIVER_VERSION);
}
if (p_registry->rsm_version != RSM_VERSION)
return (RSMERR_BAD_DRIVER_VERSION);
mutex_enter(&rsmops_lock);
pp_tail = &rsmops_drv_head;
while (*pp_tail) {
if (strcmp((*pp_tail)->drv.drv_name, p_registry->drv_name)
== 0) {
mutex_exit(&rsmops_lock);
return (RSMERR_DRIVER_NAME_IN_USE);
}
pp_tail = &((*pp_tail)->next);
}
p = kmem_alloc(sizeof (rsmops_drv_t), KM_SLEEP);
p->drv = *p_registry;
p->next = NULL;
p->ctrl_cnt = 0;
p->ctrl_head = NULL;
if (p->drv.rsm_thread_entry_pt) {
extern pri_t minclsyspri;
p->ctrl_cnt++;
p->thread_id = thread_create(NULL, 0, rsmops_thread_entry,
p, 0, &p0, TS_RUN, minclsyspri);
rsmops_threads_started++;
} else
p->thread_id = NULL;
*pp_tail = p;
mutex_exit(&rsmops_lock);
return (RSM_SUCCESS);
}
int
rsm_unregister_driver(rsmops_registry_t *p_registry)
{
rsmops_drv_t **pp_tail;
rsmops_drv_t *p;
mutex_enter(&rsmops_lock);
pp_tail = &rsmops_drv_head;
while (*pp_tail) {
if (strcmp((*pp_tail)->drv.drv_name, p_registry->drv_name)) {
pp_tail = &((*pp_tail)->next);
continue;
}
if ((*pp_tail)->ctrl_cnt) {
mutex_exit(&rsmops_lock);
return (RSMERR_CTLRS_REGISTERED);
}
p = *pp_tail;
*pp_tail = (*pp_tail)->next;
kmem_free(p, sizeof (rsmops_drv_t));
mutex_exit(&rsmops_lock);
return (RSM_SUCCESS);
}
mutex_exit(&rsmops_lock);
return (RSMERR_DRIVER_NOT_REGISTERED);
}
static rsmops_drv_t *
find_rsmpi_driver(const char *name)
{
rsmops_drv_t *p_rsmops_list;
ASSERT(MUTEX_HELD(&rsmops_lock));
for (p_rsmops_list = rsmops_drv_head; p_rsmops_list != NULL;
p_rsmops_list = p_rsmops_list->next) {
if (strcmp(name, p_rsmops_list->drv.drv_name) == 0) {
return (p_rsmops_list);
}
}
return (NULL);
}
static rsmops_ctrl_t *
find_rsmpi_controller(const char *name, uint_t number)
{
rsmops_drv_t *p_drv;
rsmops_ctrl_t *p;
ASSERT(MUTEX_HELD(&rsmops_lock));
if ((p_drv = find_rsmpi_driver(name)) == NULL)
return (NULL);
for (p = p_drv->ctrl_head; p != NULL; p = p->next) {
ASSERT(p->p_drv == p_drv);
if (p->number == number)
return (p);
}
return (NULL);
}
static rsmops_ctrl_t *
find_rsmpi_controller_handle(rsm_controller_handle_t cntlr_handle)
{
rsmops_drv_t *p_drv;
rsmops_ctrl_t *p;
ASSERT(MUTEX_HELD(&rsmops_lock));
for (p_drv = rsmops_drv_head; p_drv != NULL; p_drv = p_drv->next) {
for (p = p_drv->ctrl_head; p != NULL; p = p->next) {
if (p->handle == cntlr_handle)
return (p);
}
}
return (NULL);
}
static vnode_t *
rsmops_device_open(const char *major_name, const minor_t minor_num);
int
rsm_get_controller(const char *name, uint_t number,
rsm_controller_object_t *controller, uint_t version)
{
rsmops_ctrl_t *p_ctrl;
rsmops_drv_t *p_drv;
vnode_t *vp;
int error;
int (*rsm_get_controller_handler)
(const char *name, uint_t number,
rsm_controller_object_t *pcontroller, uint_t version);
mutex_enter(&rsmops_lock);
if ((p_ctrl = find_rsmpi_controller(name, number)) == NULL) {
if ((p_drv = find_rsmpi_driver(name)) == NULL) {
mutex_exit(&rsmops_lock);
if ((error = modload("drv", (char *)name)) == -1) {
return (RSMERR_CTLR_NOT_PRESENT);
}
mutex_enter(&rsmops_lock);
if ((p_drv = find_rsmpi_driver(name)) == NULL) {
mutex_exit(&rsmops_lock);
return (RSMERR_CTLR_NOT_PRESENT);
}
}
ASSERT(p_drv);
p_ctrl = find_rsmpi_controller(name, number);
if (p_ctrl == NULL) {
mutex_exit(&rsmops_lock);
vp = rsmops_device_open(name, number);
mutex_enter(&rsmops_lock);
if (vp != NULL) {
(void) VOP_CLOSE(vp, FREAD|FWRITE, 0, 0,
CRED(), NULL);
VN_RELE(vp);
}
p_ctrl = find_rsmpi_controller(name, number);
if (p_ctrl == NULL) {
mutex_exit(&rsmops_lock);
return (RSMERR_CTLR_NOT_PRESENT);
}
}
ASSERT(p_ctrl);
} else {
p_drv = p_ctrl->p_drv;
}
ASSERT(p_drv);
ASSERT(p_drv == p_ctrl->p_drv);
rsm_get_controller_handler = p_drv->drv.rsm_get_controller_handler;
p_ctrl->refcnt++;
mutex_exit(&rsmops_lock);
error = (*rsm_get_controller_handler)(name, number, controller,
version);
if (error != RSM_SUCCESS) {
mutex_enter(&rsmops_lock);
p_ctrl->refcnt--;
mutex_exit(&rsmops_lock);
} else {
mutex_enter(&rsmops_lock);
if ((p_ctrl = find_rsmpi_controller(name, number)) == NULL) {
mutex_exit(&rsmops_lock);
return (RSMERR_CTLR_NOT_PRESENT);
}
p_ctrl->handle = controller->handle;
mutex_exit(&rsmops_lock);
}
return (error);
}
int
rsm_release_controller(const char *name, uint_t number,
rsm_controller_object_t *controller)
{
rsmops_ctrl_t *p_ctrl;
rsmops_drv_t *p_drv;
int error;
int (*releaser)(const char *name, uint_t number,
rsm_controller_object_t *controller);
mutex_enter(&rsmops_lock);
if ((p_ctrl = find_rsmpi_controller(name, number)) == NULL) {
mutex_exit(&rsmops_lock);
return (RSMERR_CTLR_NOT_PRESENT);
}
p_drv = find_rsmpi_driver(name);
ASSERT(p_drv);
releaser = p_drv->drv.rsm_release_controller_handler;
mutex_exit(&rsmops_lock);
error = (*releaser)(name, number, controller);
if (error == RSM_SUCCESS) {
mutex_enter(&rsmops_lock);
p_ctrl->refcnt--;
mutex_exit(&rsmops_lock);
}
return (error);
}
int
rsm_register_controller(const char *name, uint_t number,
rsm_controller_attr_t *attrp)
{
rsmops_drv_t *p_drv;
rsmops_ctrl_t *p_ctrl;
if (strlen(name) > MAX_DRVNAME)
return (RSMERR_NAME_TOO_LONG);
mutex_enter(&rsmops_lock);
p_drv = find_rsmpi_driver(name);
if (p_drv == NULL) {
mutex_exit(&rsmops_lock);
return (RSMERR_DRIVER_NOT_REGISTERED);
}
p_ctrl = find_rsmpi_controller(name, number);
if (p_ctrl) {
mutex_exit(&rsmops_lock);
return (RSMERR_CTLR_ALREADY_REGISTERED);
}
p_ctrl = kmem_alloc(sizeof (rsmops_ctrl_t), KM_SLEEP);
p_drv->ctrl_cnt++;
p_ctrl->p_drv = p_drv;
p_ctrl->number = number;
p_ctrl->refcnt = 0;
p_ctrl->attrp = attrp;
p_ctrl->handle = NULL;
p_ctrl->next = p_drv->ctrl_head;
p_drv->ctrl_head = p_ctrl;
mutex_exit(&rsmops_lock);
return (RSM_SUCCESS);
}
int
rsm_unregister_controller(const char *name, uint_t number)
{
rsmops_drv_t *p_drv;
rsmops_ctrl_t **p_prev;
rsmops_ctrl_t *found;
mutex_enter(&rsmops_lock);
p_drv = find_rsmpi_driver(name);
if (p_drv == NULL) {
mutex_exit(&rsmops_lock);
return (RSMERR_DRIVER_NOT_REGISTERED);
}
for (p_prev = &p_drv->ctrl_head; *p_prev; p_prev = &((*p_prev)->next)) {
if ((*p_prev)->number == number) {
found = *p_prev;
if (found->refcnt) {
mutex_exit(&rsmops_lock);
return (RSMERR_CTLR_IN_USE);
}
*p_prev = found->next;
p_drv->ctrl_cnt--;
mutex_exit(&rsmops_lock);
kmem_free(found, sizeof (rsmops_ctrl_t));
return (RSM_SUCCESS);
}
}
mutex_exit(&rsmops_lock);
return (RSMERR_CTLR_NOT_REGISTERED);
}
static vnode_t *
rsmops_device_open(const char *major_name, const minor_t minor_num)
{
major_t maj;
vnode_t *vp;
int ret;
if (minor_num == (minor_t)-1) {
return (NULL);
}
maj = ddi_name_to_major((char *)major_name);
if (maj == (major_t)-1) {
return (NULL);
}
vp = makespecvp(makedevice(maj, minor_num), VCHR);
ret = VOP_OPEN(&vp, FREAD|FWRITE, CRED(), NULL);
if (ret == 0) {
return (vp);
} else {
VN_RELE(vp);
return (NULL);
}
}
int
rsm_get_controller_attr(rsm_controller_handle_t handle,
rsm_controller_attr_t **attrp)
{
rsmops_ctrl_t *p_ctrl;
if (handle == NULL)
return (RSMERR_BAD_CTLR_HNDL);
mutex_enter(&rsmops_lock);
if ((p_ctrl = find_rsmpi_controller_handle(handle)) == NULL) {
mutex_exit(&rsmops_lock);
return (RSMERR_BAD_CTLR_HNDL);
}
*attrp = p_ctrl->attrp;
mutex_exit(&rsmops_lock);
return (RSM_SUCCESS);
}