HelenOS sources
This source file includes following definitions.
- init_driver_list
- create_driver
- add_driver
- get_driver_info
- lookup_available_drivers
- find_best_match_driver
- attach_driver
- detach_driver
- start_driver
- stop_driver
- driver_find
- driver_find_by_name
- pass_devices_to_driver
- initialize_running_driver
- init_driver
- clean_driver
- delete_driver
- assign_driver
- add_device
- driver_dev_remove
- driver_dev_gone
- driver_fun_online
- driver_fun_offline
- driver_get_list
- driver_get_devices
- driver_reassign_fibril
#include <dirent.h>
#include <errno.h>
#include <io/log.h>
#include <vfs/vfs.h>
#include <loc.h>
#include <str_error.h>
#include <stdio.h>
#include <task.h>
#include "dev.h"
#include "devman.h"
#include "driver.h"
#include "fun.h"
#include "match.h"
#include "main.h"
static errno_t driver_reassign_fibril(void *);
void init_driver_list(driver_list_t *drv_list)
{
assert(drv_list != NULL);
list_initialize(&drv_list->drivers);
fibril_mutex_initialize(&drv_list->drivers_mutex);
drv_list->next_handle = 1;
}
driver_t *create_driver(void)
{
driver_t *res = malloc(sizeof(driver_t));
if (res != NULL)
init_driver(res);
return res;
}
void add_driver(driver_list_t *drivers_list, driver_t *drv)
{
fibril_mutex_lock(&drivers_list->drivers_mutex);
list_append(&drv->drivers, &drivers_list->drivers);
drv->handle = drivers_list->next_handle++;
fibril_mutex_unlock(&drivers_list->drivers_mutex);
log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' was added to the list of available "
"drivers.", drv->name);
}
bool get_driver_info(const char *base_path, const char *name, driver_t *drv)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "get_driver_info(base_path=\"%s\", name=\"%s\")",
base_path, name);
assert(base_path != NULL && name != NULL && drv != NULL);
bool suc = false;
char *match_path = NULL;
size_t name_size = 0;
match_path = get_abs_path(base_path, name, MATCH_EXT);
if (match_path == NULL)
goto cleanup;
if (!read_match_ids(match_path, &drv->match_ids))
goto cleanup;
name_size = str_size(name) + 1;
drv->name = malloc(name_size);
if (drv->name == NULL)
goto cleanup;
str_cpy(drv->name, name_size, name);
drv->binary_path = get_abs_path(base_path, name, "");
if (drv->binary_path == NULL)
goto cleanup;
vfs_stat_t s;
if (vfs_stat_path(drv->binary_path, &s) != EOK) {
log_msg(LOG_DEFAULT, LVL_ERROR, "Driver not found at path `%s'.",
drv->binary_path);
goto cleanup;
}
suc = true;
cleanup:
if (!suc) {
free(drv->binary_path);
free(drv->name);
init_driver(drv);
}
free(match_path);
return suc;
}
int lookup_available_drivers(driver_list_t *drivers_list, const char *dir_path)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "lookup_available_drivers(dir=\"%s\")", dir_path);
int drv_cnt = 0;
DIR *dir = NULL;
struct dirent *diren;
dir = opendir(dir_path);
if (dir != NULL) {
driver_t *drv = create_driver();
while ((diren = readdir(dir))) {
if (get_driver_info(dir_path, diren->d_name, drv)) {
add_driver(drivers_list, drv);
drv_cnt++;
drv = create_driver();
}
}
delete_driver(drv);
closedir(dir);
}
return drv_cnt;
}
driver_t *find_best_match_driver(driver_list_t *drivers_list, dev_node_t *node)
{
driver_t *best_drv = NULL;
int best_score = 0, score = 0;
int cur_score;
link_t *link;
fibril_mutex_lock(&drivers_list->drivers_mutex);
if (node->drv != NULL) {
cur_score = get_match_score(node->drv, node);
link = list_next(&node->drv->drivers, &drivers_list->drivers);
while (link != NULL) {
driver_t *drv = list_get_instance(link, driver_t,
drivers);
score = get_match_score(drv, node);
if (score == cur_score) {
fibril_mutex_unlock(&drivers_list->drivers_mutex);
return drv;
}
link = list_next(link, &drivers_list->drivers);
}
} else {
cur_score = INT_MAX;
}
list_foreach(drivers_list->drivers, drivers, driver_t, drv) {
score = get_match_score(drv, node);
if (score > best_score && score < cur_score) {
best_score = score;
best_drv = drv;
}
}
fibril_mutex_unlock(&drivers_list->drivers_mutex);
return best_drv;
}
void attach_driver(dev_tree_t *tree, dev_node_t *dev, driver_t *drv)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "attach_driver(dev=\"%s\",drv=\"%s\")",
dev->pfun->pathname, drv->name);
fibril_mutex_lock(&drv->driver_mutex);
fibril_rwlock_write_lock(&tree->rwlock);
dev->drv = drv;
dev->passed_to_driver = false;
dev->state = DEVICE_NOT_INITIALIZED;
list_append(&dev->driver_devices, &drv->devices);
fibril_rwlock_write_unlock(&tree->rwlock);
fibril_mutex_unlock(&drv->driver_mutex);
}
void detach_driver(dev_tree_t *tree, dev_node_t *dev)
{
driver_t *drv = dev->drv;
assert(drv != NULL);
log_msg(LOG_DEFAULT, LVL_DEBUG, "detach_driver(dev=\"%s\",drv=\"%s\")",
dev->pfun->pathname, drv->name);
fibril_mutex_lock(&drv->driver_mutex);
fibril_rwlock_write_lock(&tree->rwlock);
dev->drv = NULL;
list_remove(&dev->driver_devices);
fibril_rwlock_write_unlock(&tree->rwlock);
fibril_mutex_unlock(&drv->driver_mutex);
}
bool start_driver(driver_t *drv)
{
errno_t rc;
assert(fibril_mutex_is_locked(&drv->driver_mutex));
log_msg(LOG_DEFAULT, LVL_DEBUG, "start_driver(drv=\"%s\")", drv->name);
rc = task_spawnl(NULL, NULL, drv->binary_path, drv->binary_path, NULL);
if (rc != EOK) {
log_msg(LOG_DEFAULT, LVL_ERROR, "Spawning driver `%s' (%s) failed: %s.",
drv->name, drv->binary_path, str_error(rc));
return false;
}
drv->state = DRIVER_STARTING;
return true;
}
errno_t stop_driver(driver_t *drv)
{
async_exch_t *exch;
errno_t retval;
log_msg(LOG_DEFAULT, LVL_DEBUG, "stop_driver(drv=\"%s\")", drv->name);
exch = async_exchange_begin(drv->sess);
retval = async_req_0_0(exch, DRIVER_STOP);
loc_exchange_end(exch);
if (retval != EOK)
return retval;
drv->state = DRIVER_NOT_STARTED;
async_hangup(drv->sess);
drv->sess = NULL;
return EOK;
}
driver_t *driver_find(driver_list_t *drv_list, devman_handle_t handle)
{
driver_t *res = NULL;
fibril_mutex_lock(&drv_list->drivers_mutex);
list_foreach(drv_list->drivers, drivers, driver_t, drv) {
if (drv->handle == handle) {
res = drv;
break;
}
}
fibril_mutex_unlock(&drv_list->drivers_mutex);
return res;
}
driver_t *driver_find_by_name(driver_list_t *drv_list, const char *drv_name)
{
driver_t *res = NULL;
fibril_mutex_lock(&drv_list->drivers_mutex);
list_foreach(drv_list->drivers, drivers, driver_t, drv) {
if (str_cmp(drv->name, drv_name) == 0) {
res = drv;
break;
}
}
fibril_mutex_unlock(&drv_list->drivers_mutex);
return res;
}
static void pass_devices_to_driver(driver_t *driver, dev_tree_t *tree)
{
dev_node_t *dev;
link_t *link;
log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver(driver=\"%s\")",
driver->name);
fibril_mutex_lock(&driver->driver_mutex);
link = driver->devices.head.next;
while (link != &driver->devices.head) {
dev = list_get_instance(link, dev_node_t, driver_devices);
fibril_rwlock_write_lock(&tree->rwlock);
if (dev->passed_to_driver) {
fibril_rwlock_write_unlock(&tree->rwlock);
link = link->next;
continue;
}
dev_add_ref(dev);
fibril_mutex_unlock(&driver->driver_mutex);
fibril_rwlock_write_unlock(&tree->rwlock);
add_device(driver, dev, tree);
if (dev->state == DEVICE_NOT_PRESENT) {
fibril_mutex_lock(&driver->driver_mutex);
list_remove(&dev->driver_devices);
fibril_mutex_unlock(&driver->driver_mutex);
dev_add_ref(dev);
fid_t fid = fibril_create(driver_reassign_fibril, dev);
if (fid == 0) {
log_msg(LOG_DEFAULT, LVL_ERROR,
"Error creating fibril to assign driver.");
dev_del_ref(dev);
}
fibril_add_ready(fid);
}
dev_del_ref(dev);
fibril_mutex_lock(&driver->driver_mutex);
link = driver->devices.head.next;
}
log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' enters running state.", driver->name);
driver->state = DRIVER_RUNNING;
fibril_mutex_unlock(&driver->driver_mutex);
}
void initialize_running_driver(driver_t *driver, dev_tree_t *tree)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "initialize_running_driver(driver=\"%s\")",
driver->name);
pass_devices_to_driver(driver, tree);
}
void init_driver(driver_t *drv)
{
assert(drv != NULL);
memset(drv, 0, sizeof(driver_t));
list_initialize(&drv->match_ids.ids);
list_initialize(&drv->devices);
fibril_mutex_initialize(&drv->driver_mutex);
drv->sess = NULL;
}
void clean_driver(driver_t *drv)
{
assert(drv != NULL);
free(drv->name);
free(drv->binary_path);
clean_match_ids(&drv->match_ids);
init_driver(drv);
}
void delete_driver(driver_t *drv)
{
assert(drv != NULL);
clean_driver(drv);
free(drv);
}
bool assign_driver(dev_node_t *dev, driver_list_t *drivers_list,
dev_tree_t *tree)
{
driver_t *drv;
assert(dev != NULL);
assert(drivers_list != NULL);
assert(tree != NULL);
again:
drv = find_best_match_driver(drivers_list, dev);
if (drv == NULL) {
log_msg(LOG_DEFAULT, LVL_ERROR, "No driver found for device `%s'.",
dev->pfun->pathname);
return false;
}
attach_driver(tree, dev, drv);
fibril_mutex_lock(&drv->driver_mutex);
if (drv->state == DRIVER_NOT_STARTED) {
start_driver(drv);
}
bool is_running = drv->state == DRIVER_RUNNING;
fibril_mutex_unlock(&drv->driver_mutex);
if (is_running) {
add_device(drv, dev, tree);
if (dev->state == DEVICE_NOT_PRESENT)
goto again;
}
fibril_mutex_lock(&drv->driver_mutex);
fibril_mutex_unlock(&drv->driver_mutex);
fibril_rwlock_write_lock(&tree->rwlock);
if (dev->pfun != NULL) {
dev->pfun->state = FUN_ON_LINE;
}
fibril_rwlock_write_unlock(&tree->rwlock);
return true;
}
void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
{
log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
drv->name, dev->pfun->name);
devman_handle_t parent_handle;
if (dev->pfun) {
parent_handle = dev->pfun->handle;
} else {
parent_handle = 0;
}
async_exch_t *exch = async_exchange_begin(drv->sess);
ipc_call_t answer;
aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
parent_handle, &answer);
errno_t rc = async_data_write_start(exch, dev->pfun->name,
str_size(dev->pfun->name) + 1);
async_exchange_end(exch);
if (rc != EOK) {
async_forget(req);
} else {
async_wait_for(req, &rc);
}
switch (rc) {
case EOK:
dev->state = DEVICE_USABLE;
break;
case ENOENT:
dev->state = DEVICE_NOT_PRESENT;
break;
default:
dev->state = DEVICE_INVALID;
break;
}
dev->passed_to_driver = true;
}
errno_t driver_dev_remove(dev_tree_t *tree, dev_node_t *dev)
{
async_exch_t *exch;
errno_t retval;
driver_t *drv;
devman_handle_t handle;
assert(dev != NULL);
log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_remove(%p)", dev);
fibril_rwlock_read_lock(&tree->rwlock);
drv = dev->drv;
handle = dev->handle;
fibril_rwlock_read_unlock(&tree->rwlock);
exch = async_exchange_begin(drv->sess);
retval = async_req_1_0(exch, DRIVER_DEV_REMOVE, handle);
async_exchange_end(exch);
return retval;
}
errno_t driver_dev_gone(dev_tree_t *tree, dev_node_t *dev)
{
async_exch_t *exch;
errno_t retval;
driver_t *drv;
devman_handle_t handle;
assert(dev != NULL);
log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_gone(%p)", dev);
fibril_rwlock_read_lock(&tree->rwlock);
drv = dev->drv;
handle = dev->handle;
fibril_rwlock_read_unlock(&tree->rwlock);
exch = async_exchange_begin(drv->sess);
retval = async_req_1_0(exch, DRIVER_DEV_GONE, handle);
async_exchange_end(exch);
return retval;
}
errno_t driver_fun_online(dev_tree_t *tree, fun_node_t *fun)
{
async_exch_t *exch;
errno_t retval;
driver_t *drv;
devman_handle_t handle;
log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_online(%p)", fun);
fibril_rwlock_read_lock(&tree->rwlock);
if (fun->dev == NULL) {
fibril_rwlock_read_unlock(&tree->rwlock);
return EINVAL;
}
drv = fun->dev->drv;
handle = fun->handle;
fibril_rwlock_read_unlock(&tree->rwlock);
exch = async_exchange_begin(drv->sess);
retval = async_req_1_0(exch, DRIVER_FUN_ONLINE, handle);
loc_exchange_end(exch);
return retval;
}
errno_t driver_fun_offline(dev_tree_t *tree, fun_node_t *fun)
{
async_exch_t *exch;
errno_t retval;
driver_t *drv;
devman_handle_t handle;
log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_offline(%p)", fun);
fibril_rwlock_read_lock(&tree->rwlock);
if (fun->dev == NULL) {
fibril_rwlock_read_unlock(&tree->rwlock);
return EINVAL;
}
drv = fun->dev->drv;
handle = fun->handle;
fibril_rwlock_read_unlock(&tree->rwlock);
exch = async_exchange_begin(drv->sess);
retval = async_req_1_0(exch, DRIVER_FUN_OFFLINE, handle);
loc_exchange_end(exch);
return retval;
}
errno_t driver_get_list(driver_list_t *driver_list, devman_handle_t *hdl_buf,
size_t buf_size, size_t *act_size)
{
size_t act_cnt;
size_t buf_cnt;
fibril_mutex_lock(&driver_list->drivers_mutex);
buf_cnt = buf_size / sizeof(devman_handle_t);
act_cnt = list_count(&driver_list->drivers);
*act_size = act_cnt * sizeof(devman_handle_t);
if (buf_size % sizeof(devman_handle_t) != 0) {
fibril_mutex_unlock(&driver_list->drivers_mutex);
return EINVAL;
}
size_t pos = 0;
list_foreach(driver_list->drivers, drivers, driver_t, drv) {
if (pos < buf_cnt) {
hdl_buf[pos] = drv->handle;
}
pos++;
}
fibril_mutex_unlock(&driver_list->drivers_mutex);
return EOK;
}
errno_t driver_get_devices(driver_t *driver, devman_handle_t *hdl_buf,
size_t buf_size, size_t *act_size)
{
size_t act_cnt;
size_t buf_cnt;
fibril_mutex_lock(&driver->driver_mutex);
buf_cnt = buf_size / sizeof(devman_handle_t);
act_cnt = list_count(&driver->devices);
*act_size = act_cnt * sizeof(devman_handle_t);
if (buf_size % sizeof(devman_handle_t) != 0) {
fibril_mutex_unlock(&driver->driver_mutex);
return EINVAL;
}
size_t pos = 0;
list_foreach(driver->devices, driver_devices, dev_node_t, dev) {
if (pos < buf_cnt) {
hdl_buf[pos] = dev->handle;
}
pos++;
}
fibril_mutex_unlock(&driver->driver_mutex);
return EOK;
}
static errno_t driver_reassign_fibril(void *arg)
{
dev_node_t *dev_node = (dev_node_t *) arg;
assign_driver(dev_node, &drivers_list, &device_tree);
dev_del_ref(dev_node);
return EOK;
}
HelenOS homepage, sources at GitHub