HelenOS sources
This source file includes following definitions.
- devman_driver_register
- devman_receive_match_id
- devman_receive_match_ids
- devman_add_function
- devman_add_function_to_cat
- devman_drv_fun_online
- devman_drv_fun_offline
- devman_remove_function
- init_running_drv
- devman_connection_driver
#include <assert.h>
#include <ipc/services.h>
#include <ns.h>
#include <async.h>
#include <stdio.h>
#include <errno.h>
#include <str_error.h>
#include <stdbool.h>
#include <fibril_synch.h>
#include <stdlib.h>
#include <str.h>
#include <io/log.h>
#include <ipc/devman.h>
#include <loc.h>
#include "client_conn.h"
#include "dev.h"
#include "devman.h"
#include "devtree.h"
#include "driver.h"
#include "drv_conn.h"
#include "fun.h"
#include "loc.h"
#include "main.h"
static errno_t init_running_drv(void *drv);
static driver_t *devman_driver_register(ipc_call_t *call)
{
driver_t *driver = NULL;
char *drv_name = NULL;
log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
errno_t rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
if (rc != EOK) {
async_answer_0(call, rc);
return NULL;
}
log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
drv_name);
driver = driver_find_by_name(&drivers_list, drv_name);
if (driver == NULL) {
log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
free(drv_name);
drv_name = NULL;
async_answer_0(call, ENOENT);
return NULL;
}
free(drv_name);
drv_name = NULL;
fibril_mutex_lock(&driver->driver_mutex);
if (driver->sess) {
log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
driver->name);
fibril_mutex_unlock(&driver->driver_mutex);
async_answer_0(call, EEXIST);
return NULL;
}
switch (driver->state) {
case DRIVER_NOT_STARTED:
log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
driver->name);
driver->state = DRIVER_STARTING;
break;
case DRIVER_STARTING:
break;
case DRIVER_RUNNING:
assert(false);
}
log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
driver->name);
driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
if (!driver->sess) {
fibril_mutex_unlock(&driver->driver_mutex);
async_answer_0(call, ENOTSUP);
return NULL;
}
async_sess_args_set(driver->sess, INTERFACE_DDF_DEVMAN, 0, 0);
log_msg(LOG_DEFAULT, LVL_NOTE,
"The `%s' driver was successfully registered as running.",
driver->name);
fid_t fid = fibril_create(init_running_drv, driver);
if (fid == 0) {
log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril "
"for driver `%s'.", driver->name);
fibril_mutex_unlock(&driver->driver_mutex);
async_answer_0(call, ENOMEM);
return NULL;
}
fibril_add_ready(fid);
fibril_mutex_unlock(&driver->driver_mutex);
async_answer_0(call, EOK);
return driver;
}
static errno_t devman_receive_match_id(match_id_list_t *match_ids)
{
match_id_t *match_id = create_match_id();
ipc_call_t call;
errno_t rc = 0;
async_get_call(&call);
if (DEVMAN_ADD_MATCH_ID != ipc_get_imethod(&call)) {
log_msg(LOG_DEFAULT, LVL_ERROR,
"Invalid protocol when trying to receive match id.");
async_answer_0(&call, EINVAL);
delete_match_id(match_id);
return EINVAL;
}
if (match_id == NULL) {
log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
async_answer_0(&call, ENOMEM);
return ENOMEM;
}
async_answer_0(&call, EOK);
match_id->score = ipc_get_arg1(&call);
char *match_id_str;
rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
match_id->id = match_id_str;
if (rc != EOK) {
delete_match_id(match_id);
log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
str_error(rc));
return rc;
}
list_append(&match_id->link, &match_ids->ids);
log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
match_id->id, match_id->score);
return rc;
}
static errno_t devman_receive_match_ids(sysarg_t match_count,
match_id_list_t *match_ids)
{
errno_t ret = EOK;
size_t i;
for (i = 0; i < match_count; i++) {
if (EOK != (ret = devman_receive_match_id(match_ids)))
return ret;
}
return ret;
}
static void devman_add_function(ipc_call_t *call)
{
fun_type_t ftype = (fun_type_t) ipc_get_arg1(call);
devman_handle_t dev_handle = ipc_get_arg2(call);
sysarg_t match_count = ipc_get_arg3(call);
dev_tree_t *tree = &device_tree;
dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
if (pdev == NULL) {
async_answer_0(call, ENOENT);
return;
}
if (ftype != fun_inner && ftype != fun_exposed) {
log_msg(LOG_DEFAULT, LVL_ERROR,
"Unknown function type %d provided by driver.",
(int) ftype);
dev_del_ref(pdev);
async_answer_0(call, EINVAL);
return;
}
char *fun_name = NULL;
errno_t rc = async_data_write_accept((void **) &fun_name, true, 0, 0, 0, 0);
if (rc != EOK) {
dev_del_ref(pdev);
async_answer_0(call, rc);
return;
}
fibril_rwlock_write_lock(&tree->rwlock);
if (pdev->state == DEVICE_REMOVED) {
fibril_rwlock_write_unlock(&tree->rwlock);
dev_del_ref(pdev);
async_answer_0(call, ENOENT);
return;
}
fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
if (tfun) {
fun_del_ref(tfun);
fibril_rwlock_write_unlock(&tree->rwlock);
dev_del_ref(pdev);
async_answer_0(call, EEXIST);
printf(NAME ": Warning, driver tried to register `%s' twice.\n",
fun_name);
free(fun_name);
return;
}
fun_node_t *fun = create_fun_node();
fun_add_ref(fun);
fun->ftype = ftype;
fun_busy_lock(fun);
if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
fibril_rwlock_write_unlock(&tree->rwlock);
dev_del_ref(pdev);
fun_busy_unlock(fun);
fun_del_ref(fun);
fun_del_ref(fun);
async_answer_0(call, ENOMEM);
return;
}
fibril_rwlock_write_unlock(&tree->rwlock);
dev_del_ref(pdev);
devman_receive_match_ids(match_count, &fun->match_ids);
rc = fun_online(fun);
if (rc != EOK) {
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_0(call, rc);
return;
}
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_1(call, EOK, fun->handle);
}
static void devman_add_function_to_cat(ipc_call_t *call)
{
devman_handle_t handle = ipc_get_arg1(call);
category_id_t cat_id;
errno_t rc;
char *cat_name;
rc = async_data_write_accept((void **) &cat_name, true,
0, 0, 0, 0);
if (rc != EOK) {
async_answer_0(call, rc);
return;
}
fun_node_t *fun = find_fun_node(&device_tree, handle);
if (fun == NULL) {
async_answer_0(call, ENOENT);
return;
}
fibril_rwlock_read_lock(&device_tree.rwlock);
if (fun->state == FUN_REMOVED) {
fibril_rwlock_read_unlock(&device_tree.rwlock);
async_answer_0(call, ENOENT);
return;
}
rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
if (rc == EOK)
rc = loc_service_add_to_cat(devman_srv, fun->service_id, cat_id);
if (rc == EOK) {
log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
fun->pathname, cat_name);
} else {
log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
"`%s'.", fun->pathname, cat_name);
}
fibril_rwlock_read_unlock(&device_tree.rwlock);
fun_del_ref(fun);
async_answer_0(call, rc);
}
static void devman_drv_fun_online(ipc_call_t *icall, driver_t *drv)
{
fun_node_t *fun;
errno_t rc;
log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
fun = find_fun_node(&device_tree, ipc_get_arg1(icall));
if (fun == NULL) {
async_answer_0(icall, ENOENT);
return;
}
fun_busy_lock(fun);
fibril_rwlock_read_lock(&device_tree.rwlock);
if (fun->dev == NULL || fun->dev->drv != drv) {
fibril_rwlock_read_unlock(&device_tree.rwlock);
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_0(icall, ENOENT);
return;
}
fibril_rwlock_read_unlock(&device_tree.rwlock);
rc = fun_online(fun);
if (rc != EOK) {
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_0(icall, rc);
return;
}
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_0(icall, EOK);
}
static void devman_drv_fun_offline(ipc_call_t *icall, driver_t *drv)
{
fun_node_t *fun;
errno_t rc;
fun = find_fun_node(&device_tree, ipc_get_arg1(icall));
if (fun == NULL) {
async_answer_0(icall, ENOENT);
return;
}
fun_busy_lock(fun);
fibril_rwlock_write_lock(&device_tree.rwlock);
if (fun->dev == NULL || fun->dev->drv != drv) {
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_0(icall, ENOENT);
return;
}
fibril_rwlock_write_unlock(&device_tree.rwlock);
rc = fun_offline(fun);
if (rc != EOK) {
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_0(icall, rc);
return;
}
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_0(icall, EOK);
}
static void devman_remove_function(ipc_call_t *call)
{
devman_handle_t fun_handle = ipc_get_arg1(call);
dev_tree_t *tree = &device_tree;
errno_t rc;
fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
if (fun == NULL) {
async_answer_0(call, ENOENT);
return;
}
fun_busy_lock(fun);
fibril_rwlock_write_lock(&tree->rwlock);
log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
if (fun->state == FUN_REMOVED) {
fibril_rwlock_write_unlock(&tree->rwlock);
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_0(call, ENOENT);
return;
}
if (fun->ftype == fun_inner) {
if (fun->child != NULL) {
dev_node_t *dev = fun->child;
device_state_t dev_state;
errno_t gone_rc;
dev_add_ref(dev);
dev_state = dev->state;
fibril_rwlock_write_unlock(&device_tree.rwlock);
if (dev_state == DEVICE_USABLE)
gone_rc = driver_dev_gone(&device_tree, dev);
else
gone_rc = EOK;
fibril_rwlock_read_lock(&device_tree.rwlock);
if (gone_rc != EOK || !list_empty(&dev->functions)) {
log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
"functions for device that is gone. "
"Device node is now defunct.");
dev->state = DEVICE_INVALID;
fibril_rwlock_read_unlock(&device_tree.rwlock);
dev_del_ref(dev);
if (gone_rc == EOK)
gone_rc = ENOTSUP;
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_0(call, gone_rc);
return;
}
driver_t *driver = dev->drv;
fibril_rwlock_read_unlock(&device_tree.rwlock);
if (driver)
detach_driver(&device_tree, dev);
fibril_rwlock_write_lock(&device_tree.rwlock);
remove_dev_node(&device_tree, dev);
dev_del_ref(dev);
dev_del_ref(dev);
}
} else {
if (fun->service_id != 0) {
rc = loc_unregister_tree_function(fun, &device_tree);
if (rc != EOK) {
log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
"service.");
fibril_rwlock_write_unlock(&tree->rwlock);
fun_busy_unlock(fun);
fun_del_ref(fun);
async_answer_0(call, EIO);
return;
}
}
}
remove_fun_node(&device_tree, fun);
fibril_rwlock_write_unlock(&tree->rwlock);
fun_busy_unlock(fun);
fun_del_ref(fun);
fun_del_ref(fun);
log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
async_answer_0(call, EOK);
}
static errno_t init_running_drv(void *drv)
{
driver_t *driver = (driver_t *) drv;
initialize_running_driver(driver, &device_tree);
log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
driver->name);
return 0;
}
void devman_connection_driver(ipc_call_t *icall, void *arg)
{
client_t *client;
driver_t *driver = NULL;
async_accept_0(icall);
client = async_get_client_data();
if (client == NULL) {
log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
return;
}
while (true) {
ipc_call_t call;
async_get_call(&call);
if (!ipc_get_imethod(&call)) {
async_answer_0(&call, EOK);
break;
}
if (ipc_get_imethod(&call) != DEVMAN_DRIVER_REGISTER) {
fibril_mutex_lock(&client->mutex);
driver = client->driver;
fibril_mutex_unlock(&client->mutex);
if (driver == NULL) {
async_answer_0(&call, ENOTSUP);
continue;
}
}
switch (ipc_get_imethod(&call)) {
case DEVMAN_DRIVER_REGISTER:
fibril_mutex_lock(&client->mutex);
if (client->driver != NULL) {
fibril_mutex_unlock(&client->mutex);
async_answer_0(&call, EINVAL);
continue;
}
client->driver = devman_driver_register(&call);
fibril_mutex_unlock(&client->mutex);
break;
case DEVMAN_ADD_FUNCTION:
devman_add_function(&call);
break;
case DEVMAN_ADD_DEVICE_TO_CATEGORY:
devman_add_function_to_cat(&call);
break;
case DEVMAN_DRV_FUN_ONLINE:
devman_drv_fun_online(&call, driver);
break;
case DEVMAN_DRV_FUN_OFFLINE:
devman_drv_fun_offline(&call, driver);
break;
case DEVMAN_REMOVE_FUNCTION:
devman_remove_function(&call);
break;
default:
async_answer_0(&call, EINVAL);
break;
}
}
}
HelenOS homepage, sources at GitHub