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

Reply via email to