Driver registration and probing is implemented for linux-gen ODP. Signed-off-by: Christophe Milard <christophe.mil...@linaro.org> --- platform/linux-generic/drv_driver.c | 349 ++++++++++++++++++++++++++++++++++-- 1 file changed, 336 insertions(+), 13 deletions(-)
diff --git a/platform/linux-generic/drv_driver.c b/platform/linux-generic/drv_driver.c index 873ec3c..ea92457 100644 --- a/platform/linux-generic/drv_driver.c +++ b/platform/linux-generic/drv_driver.c @@ -12,6 +12,7 @@ #include <odp/api/std_types.h> #include <odp/api/debug.h> #include <odp/api/rwlock_recursive.h> +#include <odp/api/ticketlock.h> #include <odp/drv/driver.h> #include <odp/drv/spec/driver.h> #include <odp_debug_internal.h> @@ -29,6 +30,11 @@ typedef struct _odpdrv_enumr_class_s _odpdrv_enumr_class_t; typedef struct _odpdrv_enumr_s _odpdrv_enumr_t; typedef struct _odpdrv_device_s _odpdrv_device_t; typedef struct _odpdrv_devio_s _odpdrv_devio_t; +typedef struct _odpdrv_driver_s _odpdrv_driver_t; + +static int unbind_device_driver(_odpdrv_device_t *dev, + void (*callback)(odpdrv_device_t odpdrv_dev), + int immediate); /* an enumerator class (list element) */ struct _odpdrv_enumr_class_s { @@ -62,6 +68,8 @@ static struct _odpdrv_enumr_lst_t enumr_lst; /* a device (list element) */ struct _odpdrv_device_s { odpdrv_device_param_t param; + _odpdrv_driver_t *driver; /* driver for the device (if bound), or NULL*/ + _odpdrv_devio_t *devio; /* devio used for device (if bound), or NULL*/ void (*enumr_destroy_callback)(void *enum_dev);/*dev destroy callback */ struct _odpdrv_device_s *next; } _odpdrv_device_s; @@ -87,6 +95,21 @@ typedef struct _odpdrv_devio_lst_t { } _odpdrv_devio_lst_t; static struct _odpdrv_devio_lst_t devio_lst; +/* a driver (list element) */ +struct _odpdrv_driver_s { + odpdrv_driver_param_t param; + _odp_ishm_pool_t *pool; + odp_ticketlock_t probelock; /* to avoid concurrent probe on same drv*/ + struct _odpdrv_driver_s *next; +}; + +/* the driver list: */ +typedef struct _odpdrv_driver_lst_t { + odp_rwlock_recursive_t lock; + _odpdrv_driver_t *head; +} _odpdrv_driver_lst_t; +static struct _odpdrv_driver_lst_t driver_lst; + /* some driver elements (such as enumeraor classes, drivers, devio) may * register before init_global and init_local complete. Mutex will fail * in this cases but should be used later on. @@ -188,6 +211,30 @@ static void devio_list_write_unlock(void) odp_rwlock_recursive_write_unlock(&devio_lst.lock); } +static void driver_list_read_lock(void) +{ + if (init_global_status == DONE) + odp_rwlock_recursive_read_lock(&driver_lst.lock); +} + +static void driver_list_read_unlock(void) +{ + if (init_global_status == DONE) + odp_rwlock_recursive_read_unlock(&driver_lst.lock); +} + +static void driver_list_write_lock(void) +{ + if (init_global_status == DONE) + odp_rwlock_recursive_write_lock(&driver_lst.lock); +} + +static void driver_list_write_unlock(void) +{ + if (init_global_status == DONE) + odp_rwlock_recursive_write_unlock(&driver_lst.lock); +} + /* some functions to get internal pointers from handles... */ static inline _odpdrv_enumr_class_t *get_enumr_class(odpdrv_enumr_class_t class) { @@ -346,6 +393,8 @@ odpdrv_device_t odpdrv_device_create(odpdrv_device_param_t *param) /* save and set dev init parameters and insert new device in list */ dev->param = *param; dev->enumr_destroy_callback = NULL; + dev->driver = NULL; + dev->devio = NULL; dev_list_write_lock(); dev->next = device_lst.head; device_lst.head = dev; @@ -399,19 +448,17 @@ int odpdrv_device_destroy(odpdrv_device_t dev, */ target->enumr_destroy_callback = callback; - /* TODO: if a driver is bound to the device, unbind it! - * passing the flag andf device_destroy_terminate() as a callback */ - - /* no driver is handling this device, or no callback was - * provided: continue removing the device: */ - device_destroy_terminate(dev); + /* unbind the driver from the device (if bound). + * The callback is always called. */ + unbind_device_driver(target, + device_destroy_terminate, + (flags & ODPDRV_DEV_DESTROY_IMMEDIATE)); return 0; } /* This function is called as a callback from the driver, when unbindind - * a device, or directely from odpdrv_device_destroy() if no driver - * was bound to the device. + * a device drom odpdrv_device_destroy() * just call the enumerator callback to cleanup the enumerator part * and free device memory */ static void device_destroy_terminate(odpdrv_device_t drv_device) @@ -532,10 +579,239 @@ odpdrv_devio_t odpdrv_devio_register(odpdrv_devio_param_t *param) odpdrv_driver_t odpdrv_driver_register(odpdrv_driver_param_t *param) { - ODP_ERR("NOT Supported yet! Driver %s Registration!\n.", - param->name); + _odpdrv_driver_t *driver; + + /* check for a few compulsory things: */ + if ((param->probe == NULL) || + (param->unbind == NULL)) + return ODPDRV_DRIVER_INVALID; + + /* parse the list of already registered drivers to make + * sure no driver with same name already exists: + */ + driver_list_read_lock(); + driver = driver_lst.head; + while (driver) { + if ((strncmp(param->name, driver->param.name, + ODPDRV_NAME_SIZE) == 0)) { + ODP_ERR("driver %s already registered!\n", + param->name); + driver_list_read_unlock(); + return ODPDRV_DRIVER_INVALID; + } + driver = driver->next; + } + driver_list_read_unlock(); + + /* allocate memory for the new driver: + * If init_global has not been done yet, then, we cannot allocate + * from any _ishm pool (ishm has not even been initialised at this + * stage...this happens when statically linked drivers + * register: their __constructor__ function is run before main() + * is called). But any malloc performed here(before init_global) + * will be inherited by any odpthreads (process or pthreads) as we + * are still running in the ODP instantiation processes and all + * other processes are guaranteed to be descendent of this one... + * If init_global has been done, then we allocate from the _ishm pool + * to guarantee visibility from any ODP thread. + */ + + if (init_global_status == UNDONE) { + driver = malloc(sizeof(_odpdrv_driver_t)); + if (!driver) + return ODPDRV_DRIVER_INVALID; + driver->pool = NULL; + } else { + driver = _odp_ishm_pool_alloc(list_elt_pool, + sizeof(_odpdrv_driver_t)); + if (!driver) { + ODP_ERR("_odp_ishm_pool_alloc failed!\n"); + return ODPDRV_DRIVER_INVALID; + } + driver->pool = list_elt_pool; + } + + /* save init parameters and insert driver in list */ + driver->param = *param; + odp_ticketlock_init(&driver->probelock); + driver_list_write_lock(); + driver->next = driver_lst.head; + driver_lst.head = driver; + driver_list_write_unlock(); + + return (odpdrv_driver_t)driver; +} + +/* Probe, if possible, the given driver with the given device: + * The driver is probed if: + * There exist a devio D such as + * -The name and version of the API provided by D matches one of the requested + * devio {name,version} requested by the driver + * -The enumerator's API (name and version) requested by D is provided + * by the enumerator which enumerated the device. + * This function will return zero if the above condition where met by some + * devio D and the driver probe function returns 0 (success). + * The function will return -1 if some devio D were found, but the driver + * returned a non-zero value when probed (for all of them). + * The function will return -2 if no devio matching the above requirement was + * found. + * The function will return -3 if the device was already bound to a driver */ +static int probe_device_driver(_odpdrv_device_t *dev, _odpdrv_driver_t *drv) +{ + int i; + int ret = -2; + _odpdrv_devio_t *devio; + _odpdrv_enumr_t *enumr; + _odpdrv_enumr_class_t *enumr_c; + + /* the device already has a driver?: end of story... */ + if (dev->driver) + return -3; + + /* look at the different devio this driver can work with: */ + for (i = 0; i < ODPDRV_MAX_DEVIOS; i++) { + /* look at each registered devios: */ + devio_list_read_lock(); + for (devio = devio_lst.head; devio; devio = devio->next) { + /* if devio is no good for this driver, keep searching*/ + if ((strncmp(drv->param.devios[i].api_name, + devio->param.api_name, + ODPDRV_NAME_SIZE) != 0) || + (drv->param.devios[i].api_version != + devio->param.api_version)) + continue; + + /* give a chance to the devio to reject the device + * if it feels it should do so: */ + if (devio->param.probe && + devio->param.probe((odpdrv_device_t)dev)) + continue; + + /* grab the device enumerator and its class: */ + enumr = get_enumr(dev->param.enumerator); + enumr_c = get_enumr_class(enumr->param.enumr_class); + + /* if devio is no good for this dev, keep searching */ + if ((strncmp(devio->param.enumr_api_name, + enumr->param.api_name, + ODPDRV_NAME_SIZE) != 0) || + (devio->param.enumr_api_version != + enumr->param.api_version)) + continue; + + /* seems we are good to probe the driver: */ + odp_ticketlock_lock(&drv->probelock); + if (drv->param.probe((odpdrv_device_t)dev, + (odpdrv_devio_t)devio, i) == 0) { + /* the driver accepts this device */ + odp_ticketlock_unlock(&drv->probelock); + devio_list_read_unlock(); + ODP_DBG("driver %s will handle device %s(%s)\n", + drv->param.name, + dev->param.address, + enumr_c->param.name); + dev->driver = drv; + dev->devio = devio; + return 0; + } + odp_ticketlock_unlock(&drv->probelock); + + /* driver did not accept the device: keep searching */ + ret = -1; + } + devio_list_read_unlock(); + } + return ret; +} + +/* an empty callback is given to the driver on unprobe, if no real callback is + * needed */ +static void empty_unbind_callback(odpdrv_device_t odpdrv_dev ODP_UNUSED) +{ +} + +/* unbind the device driver from the device (i.e. "unprobe") + * if the immediate flag is set, the unbind is requested to be immediate, + * i.e. the driver is due to call the callback within its unbind function. + * (if the flag is not set, the callback can be called later on from + * another context. Immediate unbinding may be less graceful then + * non immediate binding) + * The callback function is called in all cases (even if the device was not + * bound) + */ +static int unbind_device_driver(_odpdrv_device_t *dev, + void (*callback)(odpdrv_device_t odpdrv_dev), + int immediate) +{ + _odpdrv_driver_t *drv; + odpdrv_device_t odpdrv_dev = (odpdrv_device_t)dev; + int flg = immediate ? ODPDRV_DRV_UNBIND_IMMEDIATE : 0; + + if (!callback) + callback = empty_unbind_callback; + + drv = dev->driver; + if (!drv) { /* nothing to do */ + callback(odpdrv_dev); + return 0; + } + + /* note that we assure that a given driver will not be bound/unbound + * concurrentely - but this does not cover the callback */ + odp_ticketlock_lock(&drv->probelock); + if (drv->param.unbind(odpdrv_dev, callback, flg)) { + ODP_DBG("driver %s could not release device %s\n", + drv->param.name, + dev->param.address); + odp_ticketlock_unlock(&drv->probelock); + return -1; + } + + /* unbind succeeded */ + dev->driver = NULL; + dev->devio = NULL; + odp_ticketlock_unlock(&drv->probelock); + return 0; +} + +/* try to find a driver for the given device, trying all possible registered + * drivers against it: + * returns 0 on success or -1 on error + */ +static int probe_device(_odpdrv_device_t *dev) +{ + _odpdrv_driver_t *driver; + int ret = -1; + + /* go through the list of registered drivers: */ + driver_list_read_lock(); + driver = driver_lst.head; + while (driver) { + if (probe_device_driver(dev, driver) == 0) { + ret = 0; + break; + } + driver = driver->next; + } + driver_list_read_unlock(); + + return ret; +} + +/* try to find a driver for all the registered devices, trying all possible + * drivers-devices combinaison + */ +static void probe_all(void) +{ + _odpdrv_device_t *dev; - return ODPDRV_DRIVER_INVALID; + dev_list_read_lock(); + dev = device_lst.head; + while (dev) { + (void)probe_device(dev); + dev = dev->next; + } + dev_list_read_unlock(); } /* the following function is called each time probing is needed, i.e. @@ -571,6 +847,9 @@ void _odpdrv_driver_probe_drv_items(void) enumr = enumr->next; } enumr_list_read_unlock(); + + /* probe drivers for all devices */ + probe_all(); } int odpdrv_print_all(void) @@ -579,6 +858,7 @@ int odpdrv_print_all(void) _odpdrv_enumr_t *enumr; _odpdrv_device_t *dev; _odpdrv_devio_t *devio; + _odpdrv_driver_t *driver; /* we cannot use ODP_DBG before ODP init... */ if (init_global_status == UNDONE) @@ -618,11 +898,16 @@ int odpdrv_print_all(void) enumr = get_enumr(dev->param.enumerator); enumr_c = get_enumr_class(enumr->param.enumr_class); ODP_DBG(" device: address: %s, from enumerator class: %s " - " API: %s, Version: %d\n", + " API: %s, Version: %d, " + " handled by driver %s, with devio API: %s " + " (version %d)\n", dev->param.address, enumr_c->param.name, enumr->param.api_name, - enumr->param.api_version); + enumr->param.api_version, + dev->driver ? dev->driver->param.name : "<none>", + dev->devio ? dev->devio->param.api_name : "<none>", + dev->devio ? dev->devio->param.api_version : 0); dev = dev->next; } dev_list_read_unlock(); @@ -642,6 +927,17 @@ int odpdrv_print_all(void) } devio_list_read_unlock(); + /* print the list of registered drivers: */ + driver_list_read_lock(); + driver = driver_lst.head; + ODP_DBG("The following dev drivers have been registered:\n"); + while (driver) { + ODP_DBG(" driver: '%s'\n", + driver->param.name); + driver = driver->next; + } + driver_list_read_unlock(); + return 0; } @@ -660,6 +956,7 @@ int _odpdrv_driver_init_global(void) odp_rwlock_recursive_init(&enumr_lst.lock); odp_rwlock_recursive_init(&device_lst.lock); odp_rwlock_recursive_init(&devio_lst.lock); + odp_rwlock_recursive_init(&driver_lst.lock); /* probe things... */ _odpdrv_driver_probe_drv_items(); @@ -680,10 +977,36 @@ int _odpdrv_driver_term_global(void) _odpdrv_devio_t *devio; _odpdrv_enumr_class_t *enumr_c; _odpdrv_enumr_t *enumr; + _odpdrv_device_t *dev; + _odpdrv_driver_t *driver; if (init_global_status == UNDONE) return 0; + /* unbind any driver from any device: */ + dev_list_read_lock(); + dev = device_lst.head; + while (dev) { + unbind_device_driver(dev, NULL, 1); + dev = dev->next; + } + dev_list_read_unlock(); + + /* and remove all registered drivers: */ + driver_list_read_lock(); + while (driver_lst.head) { + driver = driver_lst.head; + if (driver->param.remove) { + if (driver->param.remove()) + ODP_ERR("driver removal indicated failure!\n"); + } + driver_lst.head = driver->next; + if (driver->pool) + _odp_ishm_pool_free(list_elt_pool, driver); + else + free(driver); + } + /* remove all devios which are registered: */ devio_list_write_lock(); while (devio_lst.head) { -- 2.7.4