@@ -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) {
Driver registration and probing is implemented for linux-gen ODP. Signed-off-by: Christophe Milard <christophe.milard@linaro.org> --- platform/linux-generic/drv_driver.c | 349 ++++++++++++++++++++++++++++++++++-- 1 file changed, 336 insertions(+), 13 deletions(-) -- 2.7.4