HelenOS sources
This source file includes following definitions.
- add_to_functions_list
- remove_from_functions_list
- driver_get_device
- driver_get_function
- driver_dev_add
- driver_dev_remove
- driver_dev_gone
- driver_fun_online
- driver_fun_offline
- driver_stop
- driver_connection_devman
- driver_connection_gen
- driver_connection_driver
- driver_connection_client
- create_device
- create_function
- delete_device
- delete_function
- dev_add_ref
- dev_del_ref
- fun_add_ref
- fun_del_ref
- ddf_dev_data_alloc
- ddf_dev_data_get
- ddf_dev_get_handle
- ddf_dev_get_name
- ddf_dev_parent_sess_get
- ddf_fun_set_name
- ddf_fun_get_dev
- ddf_fun_get_handle
- ddf_fun_create
- ddf_fun_data_alloc
- ddf_fun_data_get
- ddf_fun_get_name
- ddf_fun_destroy
- function_get_ops
- ddf_fun_bind
- ddf_fun_unbind
- ddf_fun_online
- ddf_fun_offline
- ddf_fun_add_match_id
- ddf_fun_set_ops
- ddf_fun_set_conn_handler
- function_get_default_handler
- ddf_fun_add_to_category
- ddf_driver_main
#include <assert.h>
#include <ipc/services.h>
#include <ipc/ns.h>
#include <async.h>
#include <stdio.h>
#include <errno.h>
#include <stdbool.h>
#include <fibril_synch.h>
#include <stdlib.h>
#include <str.h>
#include <str_error.h>
#include <ctype.h>
#include <inttypes.h>
#include <devman.h>
#include "dev_iface.h"
#include "ddf/driver.h"
#include "ddf/interrupt.h"
#include "private/driver.h"
static const driver_t *driver;
LIST_INITIALIZE(devices);
FIBRIL_MUTEX_INITIALIZE(devices_mutex);
LIST_INITIALIZE(functions);
FIBRIL_MUTEX_INITIALIZE(functions_mutex);
FIBRIL_RWLOCK_INITIALIZE(stopping_lock);
static bool stopping = false;
static ddf_dev_t *create_device(void);
static void delete_device(ddf_dev_t *);
static void dev_add_ref(ddf_dev_t *);
static void dev_del_ref(ddf_dev_t *);
static void fun_add_ref(ddf_fun_t *);
static void fun_del_ref(ddf_fun_t *);
static remote_handler_t *function_get_default_handler(ddf_fun_t *);
static void *function_get_ops(ddf_fun_t *, dev_inferface_idx_t);
static void add_to_functions_list(ddf_fun_t *fun)
{
fibril_mutex_lock(&functions_mutex);
list_append(&fun->link, &functions);
fibril_mutex_unlock(&functions_mutex);
}
static void remove_from_functions_list(ddf_fun_t *fun)
{
fibril_mutex_lock(&functions_mutex);
list_remove(&fun->link);
fibril_mutex_unlock(&functions_mutex);
}
static ddf_dev_t *driver_get_device(devman_handle_t handle)
{
assert(fibril_mutex_is_locked(&devices_mutex));
list_foreach(devices, link, ddf_dev_t, dev) {
if (dev->handle == handle)
return dev;
}
return NULL;
}
static ddf_fun_t *driver_get_function(devman_handle_t handle)
{
assert(fibril_mutex_is_locked(&functions_mutex));
list_foreach(functions, link, ddf_fun_t, fun) {
if (fun->handle == handle)
return fun;
}
return NULL;
}
static void driver_dev_add(ipc_call_t *icall)
{
devman_handle_t dev_handle = ipc_get_arg1(icall);
devman_handle_t parent_fun_handle = ipc_get_arg2(icall);
char *dev_name = NULL;
errno_t rc = async_data_write_accept((void **) &dev_name, true, 0, 0, 0, 0);
if (rc != EOK) {
async_answer_0(icall, rc);
return;
}
fibril_rwlock_read_lock(&stopping_lock);
if (stopping) {
fibril_rwlock_read_unlock(&stopping_lock);
async_answer_0(icall, EIO);
return;
}
ddf_dev_t *dev = create_device();
if (!dev) {
fibril_rwlock_read_unlock(&stopping_lock);
free(dev_name);
async_answer_0(icall, ENOMEM);
return;
}
dev->handle = dev_handle;
dev->name = dev_name;
(void) parent_fun_handle;
errno_t res = driver->driver_ops->dev_add(dev);
if (res != EOK) {
fibril_rwlock_read_unlock(&stopping_lock);
dev_del_ref(dev);
async_answer_0(icall, res);
return;
}
fibril_mutex_lock(&devices_mutex);
list_append(&dev->link, &devices);
fibril_mutex_unlock(&devices_mutex);
fibril_rwlock_read_unlock(&stopping_lock);
async_answer_0(icall, res);
}
static void driver_dev_remove(ipc_call_t *icall)
{
devman_handle_t devh = ipc_get_arg1(icall);
fibril_mutex_lock(&devices_mutex);
ddf_dev_t *dev = driver_get_device(devh);
if (dev != NULL)
dev_add_ref(dev);
fibril_mutex_unlock(&devices_mutex);
if (dev == NULL) {
async_answer_0(icall, ENOENT);
return;
}
errno_t rc;
if (driver->driver_ops->dev_remove != NULL)
rc = driver->driver_ops->dev_remove(dev);
else
rc = ENOTSUP;
if (rc == EOK) {
fibril_mutex_lock(&devices_mutex);
list_remove(&dev->link);
fibril_mutex_unlock(&devices_mutex);
dev_del_ref(dev);
}
dev_del_ref(dev);
async_answer_0(icall, rc);
}
static void driver_dev_gone(ipc_call_t *icall)
{
devman_handle_t devh = ipc_get_arg1(icall);
fibril_mutex_lock(&devices_mutex);
ddf_dev_t *dev = driver_get_device(devh);
if (dev != NULL)
dev_add_ref(dev);
fibril_mutex_unlock(&devices_mutex);
if (dev == NULL) {
async_answer_0(icall, ENOENT);
return;
}
errno_t rc;
if (driver->driver_ops->dev_gone != NULL)
rc = driver->driver_ops->dev_gone(dev);
else
rc = ENOTSUP;
if (rc == EOK) {
fibril_mutex_lock(&devices_mutex);
list_remove(&dev->link);
fibril_mutex_unlock(&devices_mutex);
dev_del_ref(dev);
}
dev_del_ref(dev);
async_answer_0(icall, rc);
}
static void driver_fun_online(ipc_call_t *icall)
{
devman_handle_t funh = ipc_get_arg1(icall);
fibril_mutex_lock(&functions_mutex);
ddf_fun_t *fun = driver_get_function(funh);
if (fun != NULL)
fun_add_ref(fun);
fibril_mutex_unlock(&functions_mutex);
if (fun == NULL) {
async_answer_0(icall, ENOENT);
return;
}
errno_t rc;
if (driver->driver_ops->fun_online != NULL)
rc = driver->driver_ops->fun_online(fun);
else
rc = ENOTSUP;
fun_del_ref(fun);
async_answer_0(icall, rc);
}
static void driver_fun_offline(ipc_call_t *icall)
{
devman_handle_t funh = ipc_get_arg1(icall);
fibril_mutex_lock(&functions_mutex);
ddf_fun_t *fun = driver_get_function(funh);
if (fun != NULL)
fun_add_ref(fun);
fibril_mutex_unlock(&functions_mutex);
if (fun == NULL) {
async_answer_0(icall, ENOENT);
return;
}
errno_t rc;
if (driver->driver_ops->fun_offline != NULL)
rc = driver->driver_ops->fun_offline(fun);
else
rc = ENOTSUP;
async_answer_0(icall, rc);
}
static void driver_stop(ipc_call_t *icall)
{
fibril_rwlock_write_lock(&stopping_lock);
stopping = true;
fibril_mutex_lock(&devices_mutex);
if (list_first(&devices) != NULL) {
fibril_mutex_unlock(&devices_mutex);
stopping = false;
fibril_rwlock_write_unlock(&stopping_lock);
async_answer_0(icall, EBUSY);
return;
}
fibril_rwlock_write_unlock(&stopping_lock);
fibril_mutex_lock(&functions_mutex);
assert(list_first(&functions) == NULL);
fibril_mutex_unlock(&functions_mutex);
async_answer_0(icall, EOK);
exit(0);
}
static void driver_connection_devman(ipc_call_t *icall, void *arg)
{
async_accept_0(icall);
while (true) {
ipc_call_t call;
async_get_call(&call);
if (!ipc_get_imethod(&call)) {
async_answer_0(&call, EOK);
break;
}
switch (ipc_get_imethod(&call)) {
case DRIVER_DEV_ADD:
driver_dev_add(&call);
break;
case DRIVER_DEV_REMOVE:
driver_dev_remove(&call);
break;
case DRIVER_DEV_GONE:
driver_dev_gone(&call);
break;
case DRIVER_FUN_ONLINE:
driver_fun_online(&call);
break;
case DRIVER_FUN_OFFLINE:
driver_fun_offline(&call);
break;
case DRIVER_STOP:
driver_stop(&call);
break;
default:
async_answer_0(&call, ENOTSUP);
}
}
}
static void driver_connection_gen(ipc_call_t *icall, bool drv)
{
devman_handle_t handle = ipc_get_arg2(icall);
fibril_mutex_lock(&functions_mutex);
ddf_fun_t *fun = driver_get_function(handle);
if (fun != NULL)
fun_add_ref(fun);
fibril_mutex_unlock(&functions_mutex);
if (fun == NULL) {
printf("%s: driver_connection_gen error - no function with handle"
" %" PRIun " was found.\n", driver->name, handle);
async_answer_0(icall, ENOENT);
return;
}
if (fun->conn_handler != NULL) {
(*fun->conn_handler)(icall, (void *)fun);
fun_del_ref(fun);
return;
}
errno_t ret = EOK;
if (fun->ops != NULL && fun->ops->open != NULL)
ret = (*fun->ops->open)(fun);
if (ret != EOK) {
async_answer_0(icall, ret);
fun_del_ref(fun);
return;
}
async_accept_0(icall);
while (true) {
ipc_call_t call;
async_get_call(&call);
sysarg_t method = ipc_get_imethod(&call);
if (!method) {
if (fun->ops != NULL && fun->ops->close != NULL)
(*fun->ops->close)(fun);
async_answer_0(&call, EOK);
fun_del_ref(fun);
return;
}
int iface_idx = DEV_IFACE_IDX(method);
if (!is_valid_iface_idx(iface_idx)) {
remote_handler_t *default_handler =
function_get_default_handler(fun);
if (default_handler != NULL) {
(*default_handler)(fun, &call);
continue;
}
printf("%s: driver_connection_gen error - "
"invalid interface id %d.",
driver->name, iface_idx);
async_answer_0(&call, ENOTSUP);
continue;
}
void *ops = function_get_ops(fun, iface_idx);
if (ops == NULL) {
printf("%s: driver_connection_gen error - ", driver->name);
printf("Function with handle %" PRIun " has no interface "
"with id %d.\n", handle, iface_idx);
async_answer_0(&call, ENOTSUP);
continue;
}
const remote_iface_t *rem_iface = get_remote_iface(iface_idx);
assert(rem_iface != NULL);
sysarg_t iface_method_idx = ipc_get_arg1(&call);
remote_iface_func_ptr_t iface_method_ptr =
get_remote_method(rem_iface, iface_method_idx);
if (iface_method_ptr == NULL) {
printf("%s: driver_connection_gen error - "
"invalid interface method.", driver->name);
async_answer_0(&call, ENOTSUP);
continue;
}
(*iface_method_ptr)(fun, ops, &call);
}
}
static void driver_connection_driver(ipc_call_t *icall, void *arg)
{
driver_connection_gen(icall, true);
}
static void driver_connection_client(ipc_call_t *icall, void *arg)
{
driver_connection_gen(icall, false);
}
static ddf_dev_t *create_device(void)
{
ddf_dev_t *dev;
dev = calloc(1, sizeof(ddf_dev_t));
if (dev == NULL)
return NULL;
refcount_init(&dev->refcnt);
return dev;
}
static ddf_fun_t *create_function(void)
{
ddf_fun_t *fun;
fun = calloc(1, sizeof(ddf_fun_t));
if (fun == NULL)
return NULL;
refcount_init(&fun->refcnt);
init_match_ids(&fun->match_ids);
link_initialize(&fun->link);
return fun;
}
static void delete_device(ddf_dev_t *dev)
{
if (dev->parent_sess)
async_hangup(dev->parent_sess);
if (dev->driver_data != NULL)
free(dev->driver_data);
if (dev->name)
free(dev->name);
free(dev);
}
static void delete_function(ddf_fun_t *fun)
{
clean_match_ids(&fun->match_ids);
if (fun->driver_data != NULL)
free(fun->driver_data);
if (fun->name != NULL)
free(fun->name);
free(fun);
}
static void dev_add_ref(ddf_dev_t *dev)
{
refcount_up(&dev->refcnt);
}
static void dev_del_ref(ddf_dev_t *dev)
{
if (refcount_down(&dev->refcnt))
delete_device(dev);
}
static void fun_add_ref(ddf_fun_t *fun)
{
dev_add_ref(fun->dev);
refcount_up(&fun->refcnt);
}
static void fun_del_ref(ddf_fun_t *fun)
{
ddf_dev_t *dev = fun->dev;
if (refcount_down(&fun->refcnt))
delete_function(fun);
dev_del_ref(dev);
}
void *ddf_dev_data_alloc(ddf_dev_t *dev, size_t size)
{
assert(dev->driver_data == NULL);
void *data = calloc(1, size);
if (data == NULL)
return NULL;
dev->driver_data = data;
return data;
}
void *ddf_dev_data_get(ddf_dev_t *dev)
{
return dev->driver_data;
}
devman_handle_t ddf_dev_get_handle(ddf_dev_t *dev)
{
return dev->handle;
}
const char *ddf_dev_get_name(ddf_dev_t *dev)
{
return dev->name;
}
async_sess_t *ddf_dev_parent_sess_get(ddf_dev_t *dev)
{
if (dev->parent_sess == NULL) {
dev->parent_sess = devman_parent_device_connect(dev->handle,
IPC_FLAG_BLOCKING);
}
return dev->parent_sess;
}
errno_t ddf_fun_set_name(ddf_fun_t *dev, const char *name)
{
assert(dev->name == NULL);
dev->name = str_dup(name);
if (dev->name == NULL)
return ENOENT;
return EOK;
}
ddf_dev_t *ddf_fun_get_dev(ddf_fun_t *fun)
{
return fun->dev;
}
devman_handle_t ddf_fun_get_handle(ddf_fun_t *fun)
{
return fun->handle;
}
ddf_fun_t *ddf_fun_create(ddf_dev_t *dev, fun_type_t ftype, const char *name)
{
ddf_fun_t *fun = create_function();
if (fun == NULL)
return NULL;
fun->dev = dev;
dev_add_ref(fun->dev);
fun->bound = false;
fun->ftype = ftype;
if (name != NULL) {
fun->name = str_dup(name);
if (fun->name == NULL) {
fun_del_ref(fun);
return NULL;
}
}
return fun;
}
void *ddf_fun_data_alloc(ddf_fun_t *fun, size_t size)
{
assert(fun->bound == false);
assert(fun->driver_data == NULL);
void *data = calloc(1, size);
if (data == NULL)
return NULL;
fun->driver_data = data;
return data;
}
void *ddf_fun_data_get(ddf_fun_t *fun)
{
return fun->driver_data;
}
const char *ddf_fun_get_name(ddf_fun_t *fun)
{
return fun->name;
}
void ddf_fun_destroy(ddf_fun_t *fun)
{
assert(fun->bound == false);
fun_del_ref(fun);
}
static void *function_get_ops(ddf_fun_t *fun, dev_inferface_idx_t idx)
{
assert(is_valid_iface_idx(idx));
if (fun->ops == NULL)
return NULL;
return fun->ops->interfaces[idx];
}
errno_t ddf_fun_bind(ddf_fun_t *fun)
{
assert(fun->bound == false);
assert(fun->name != NULL);
assert(fun->dev != NULL);
add_to_functions_list(fun);
errno_t res = devman_add_function(fun->name, fun->ftype, &fun->match_ids,
fun->dev->handle, &fun->handle);
if (res != EOK) {
remove_from_functions_list(fun);
return res;
}
fun->bound = true;
return res;
}
errno_t ddf_fun_unbind(ddf_fun_t *fun)
{
assert(fun->bound == true);
errno_t res = devman_remove_function(fun->handle);
if (res != EOK)
return res;
remove_from_functions_list(fun);
fun->bound = false;
return EOK;
}
errno_t ddf_fun_online(ddf_fun_t *fun)
{
assert(fun->bound == true);
errno_t res = devman_drv_fun_online(fun->handle);
if (res != EOK)
return res;
return EOK;
}
errno_t ddf_fun_offline(ddf_fun_t *fun)
{
assert(fun->bound == true);
errno_t res = devman_drv_fun_offline(fun->handle);
if (res != EOK)
return res;
return EOK;
}
errno_t ddf_fun_add_match_id(ddf_fun_t *fun, const char *match_id_str,
int match_score)
{
assert(fun->bound == false);
assert(fun->ftype == fun_inner);
match_id_t *match_id = create_match_id();
if (match_id == NULL)
return ENOMEM;
match_id->id = str_dup(match_id_str);
match_id->score = match_score;
add_match_id(&fun->match_ids, match_id);
return EOK;
}
void ddf_fun_set_ops(ddf_fun_t *fun, const ddf_dev_ops_t *dev_ops)
{
assert(fun->conn_handler == NULL);
fun->ops = dev_ops;
}
void ddf_fun_set_conn_handler(ddf_fun_t *fun, async_port_handler_t conn)
{
assert(fun->ops == NULL);
fun->conn_handler = conn;
}
static remote_handler_t *function_get_default_handler(ddf_fun_t *fun)
{
if (fun->ops == NULL)
return NULL;
return fun->ops->default_handler;
}
errno_t ddf_fun_add_to_category(ddf_fun_t *fun, const char *cat_name)
{
assert(fun->bound == true);
assert(fun->ftype == fun_exposed);
return devman_add_device_to_category(fun->handle, cat_name);
}
errno_t ddf_driver_main(const driver_t *drv)
{
driver = drv;
port_id_t port;
errno_t rc = async_create_port(INTERFACE_DDF_DRIVER, driver_connection_driver,
NULL, &port);
if (rc != EOK) {
printf("Error: Failed to create driver port.\n");
return rc;
}
rc = async_create_port(INTERFACE_DDF_DEVMAN, driver_connection_devman,
NULL, &port);
if (rc != EOK) {
printf("Error: Failed to create devman port.\n");
return rc;
}
async_set_fallback_port_handler(driver_connection_client, NULL);
rc = devman_driver_register(driver->name);
if (rc != EOK) {
printf("Error: Failed to register driver with device manager "
"(%s).\n", (rc == EEXIST) ? "driver already started" :
str_error(rc));
return rc;
}
rc = task_retval(0);
if (rc != EOK) {
printf("Error: Failed returning task value.\n");
return rc;
}
async_manager();
return EOK;
}
HelenOS homepage, sources at GitHub