| 1 | /*
 | 
|---|
| 2 |  * Copyright (c) 2010 Lenka Trochtova
 | 
|---|
| 3 |  * All rights reserved.
 | 
|---|
| 4 |  *
 | 
|---|
| 5 |  * Redistribution and use in source and binary forms, with or without
 | 
|---|
| 6 |  * modification, are permitted provided that the following conditions
 | 
|---|
| 7 |  * are met:
 | 
|---|
| 8 |  *
 | 
|---|
| 9 |  * - Redistributions of source code must retain the above copyright
 | 
|---|
| 10 |  *   notice, this list of conditions and the following disclaimer.
 | 
|---|
| 11 |  * - Redistributions in binary form must reproduce the above copyright
 | 
|---|
| 12 |  *   notice, this list of conditions and the following disclaimer in the
 | 
|---|
| 13 |  *   documentation and/or other materials provided with the distribution.
 | 
|---|
| 14 |  * - The name of the author may not be used to endorse or promote products
 | 
|---|
| 15 |  *   derived from this software without specific prior written permission.
 | 
|---|
| 16 |  *
 | 
|---|
| 17 |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 | 
|---|
| 18 |  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | 
|---|
| 19 |  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | 
|---|
| 20 |  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
|---|
| 21 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | 
|---|
| 22 |  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
|---|
| 23 |  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
|---|
| 24 |  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
|---|
| 25 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | 
|---|
| 26 |  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
|---|
| 27 |  */
 | 
|---|
| 28 | 
 | 
|---|
| 29 | /** @addtogroup devman
 | 
|---|
| 30 |  * @{
 | 
|---|
| 31 |  */
 | 
|---|
| 32 | 
 | 
|---|
| 33 | #include <dirent.h>
 | 
|---|
| 34 | #include <errno.h>
 | 
|---|
| 35 | #include <io/log.h>
 | 
|---|
| 36 | #include <vfs/vfs.h>
 | 
|---|
| 37 | #include <loc.h>
 | 
|---|
| 38 | #include <str_error.h>
 | 
|---|
| 39 | #include <stdio.h>
 | 
|---|
| 40 | #include <task.h>
 | 
|---|
| 41 | 
 | 
|---|
| 42 | #include "dev.h"
 | 
|---|
| 43 | #include "devman.h"
 | 
|---|
| 44 | #include "driver.h"
 | 
|---|
| 45 | #include "match.h"
 | 
|---|
| 46 | 
 | 
|---|
| 47 | /**
 | 
|---|
| 48 |  * Initialize the list of device driver's.
 | 
|---|
| 49 |  *
 | 
|---|
| 50 |  * @param drv_list the list of device driver's.
 | 
|---|
| 51 |  *
 | 
|---|
| 52 |  */
 | 
|---|
| 53 | void init_driver_list(driver_list_t *drv_list)
 | 
|---|
| 54 | {
 | 
|---|
| 55 |         assert(drv_list != NULL);
 | 
|---|
| 56 | 
 | 
|---|
| 57 |         list_initialize(&drv_list->drivers);
 | 
|---|
| 58 |         fibril_mutex_initialize(&drv_list->drivers_mutex);
 | 
|---|
| 59 |         drv_list->next_handle = 1;
 | 
|---|
| 60 | }
 | 
|---|
| 61 | 
 | 
|---|
| 62 | /** Allocate and initialize a new driver structure.
 | 
|---|
| 63 |  *
 | 
|---|
| 64 |  * @return      Driver structure.
 | 
|---|
| 65 |  */
 | 
|---|
| 66 | driver_t *create_driver(void)
 | 
|---|
| 67 | {
 | 
|---|
| 68 |         driver_t *res = malloc(sizeof(driver_t));
 | 
|---|
| 69 |         if (res != NULL)
 | 
|---|
| 70 |                 init_driver(res);
 | 
|---|
| 71 |         return res;
 | 
|---|
| 72 | }
 | 
|---|
| 73 | 
 | 
|---|
| 74 | /** Add a driver to the list of drivers.
 | 
|---|
| 75 |  *
 | 
|---|
| 76 |  * @param drivers_list  List of drivers.
 | 
|---|
| 77 |  * @param drv           Driver structure.
 | 
|---|
| 78 |  */
 | 
|---|
| 79 | void add_driver(driver_list_t *drivers_list, driver_t *drv)
 | 
|---|
| 80 | {
 | 
|---|
| 81 |         fibril_mutex_lock(&drivers_list->drivers_mutex);
 | 
|---|
| 82 |         list_append(&drv->drivers, &drivers_list->drivers);
 | 
|---|
| 83 |         drv->handle = drivers_list->next_handle++;
 | 
|---|
| 84 |         fibril_mutex_unlock(&drivers_list->drivers_mutex);
 | 
|---|
| 85 | 
 | 
|---|
| 86 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' was added to the list of available "
 | 
|---|
| 87 |             "drivers.", drv->name);
 | 
|---|
| 88 | }
 | 
|---|
| 89 | 
 | 
|---|
| 90 | /**
 | 
|---|
| 91 |  * Get information about a driver.
 | 
|---|
| 92 |  *
 | 
|---|
| 93 |  * Each driver has its own directory in the base directory.
 | 
|---|
| 94 |  * The name of the driver's directory is the same as the name of the driver.
 | 
|---|
| 95 |  * The driver's directory contains driver's binary (named as the driver without
 | 
|---|
| 96 |  * extension) and the configuration file with match ids for device-to-driver
 | 
|---|
| 97 |  *  matching (named as the driver with a special extension).
 | 
|---|
| 98 |  *
 | 
|---|
| 99 |  * This function searches for the driver's directory and containing
 | 
|---|
| 100 |  * configuration files. If all the files needed are found, they are parsed and
 | 
|---|
| 101 |  * the information about the driver is stored in the driver's structure.
 | 
|---|
| 102 |  *
 | 
|---|
| 103 |  * @param base_path     The base directory, in which we look for driver's
 | 
|---|
| 104 |  *                      subdirectory.
 | 
|---|
| 105 |  * @param name          The name of the driver.
 | 
|---|
| 106 |  * @param drv           The driver structure to fill information in.
 | 
|---|
| 107 |  *
 | 
|---|
| 108 |  * @return              True on success, false otherwise.
 | 
|---|
| 109 |  */
 | 
|---|
| 110 | bool get_driver_info(const char *base_path, const char *name, driver_t *drv)
 | 
|---|
| 111 | {
 | 
|---|
| 112 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "get_driver_info(base_path=\"%s\", name=\"%s\")",
 | 
|---|
| 113 |             base_path, name);
 | 
|---|
| 114 | 
 | 
|---|
| 115 |         assert(base_path != NULL && name != NULL && drv != NULL);
 | 
|---|
| 116 | 
 | 
|---|
| 117 |         bool suc = false;
 | 
|---|
| 118 |         char *match_path = NULL;
 | 
|---|
| 119 |         size_t name_size = 0;
 | 
|---|
| 120 | 
 | 
|---|
| 121 |         /* Read the list of match ids from the driver's configuration file. */
 | 
|---|
| 122 |         match_path = get_abs_path(base_path, name, MATCH_EXT);
 | 
|---|
| 123 |         if (match_path == NULL)
 | 
|---|
| 124 |                 goto cleanup;
 | 
|---|
| 125 | 
 | 
|---|
| 126 |         if (!read_match_ids(match_path, &drv->match_ids))
 | 
|---|
| 127 |                 goto cleanup;
 | 
|---|
| 128 | 
 | 
|---|
| 129 |         /* Allocate and fill driver's name. */
 | 
|---|
| 130 |         name_size = str_size(name) + 1;
 | 
|---|
| 131 |         drv->name = malloc(name_size);
 | 
|---|
| 132 |         if (drv->name == NULL)
 | 
|---|
| 133 |                 goto cleanup;
 | 
|---|
| 134 |         str_cpy(drv->name, name_size, name);
 | 
|---|
| 135 | 
 | 
|---|
| 136 |         /* Initialize path with driver's binary. */
 | 
|---|
| 137 |         drv->binary_path = get_abs_path(base_path, name, "");
 | 
|---|
| 138 |         if (drv->binary_path == NULL)
 | 
|---|
| 139 |                 goto cleanup;
 | 
|---|
| 140 | 
 | 
|---|
| 141 |         /* Check whether the driver's binary exists. */
 | 
|---|
| 142 |         vfs_stat_t s;
 | 
|---|
| 143 |         if (vfs_stat_path(drv->binary_path, &s) != EOK) {
 | 
|---|
| 144 |                 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver not found at path `%s'.",
 | 
|---|
| 145 |                     drv->binary_path);
 | 
|---|
| 146 |                 goto cleanup;
 | 
|---|
| 147 |         }
 | 
|---|
| 148 | 
 | 
|---|
| 149 |         suc = true;
 | 
|---|
| 150 | 
 | 
|---|
| 151 | cleanup:
 | 
|---|
| 152 |         if (!suc) {
 | 
|---|
| 153 |                 free(drv->binary_path);
 | 
|---|
| 154 |                 free(drv->name);
 | 
|---|
| 155 |                 /* Set the driver structure to the default state. */
 | 
|---|
| 156 |                 init_driver(drv);
 | 
|---|
| 157 |         }
 | 
|---|
| 158 | 
 | 
|---|
| 159 |         free(match_path);
 | 
|---|
| 160 | 
 | 
|---|
| 161 |         return suc;
 | 
|---|
| 162 | }
 | 
|---|
| 163 | 
 | 
|---|
| 164 | /** Lookup drivers in the directory.
 | 
|---|
| 165 |  *
 | 
|---|
| 166 |  * @param drivers_list  The list of available drivers.
 | 
|---|
| 167 |  * @param dir_path      The path to the directory where we search for drivers.
 | 
|---|
| 168 |  * @return              Number of drivers which were found.
 | 
|---|
| 169 |  */
 | 
|---|
| 170 | int lookup_available_drivers(driver_list_t *drivers_list, const char *dir_path)
 | 
|---|
| 171 | {
 | 
|---|
| 172 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "lookup_available_drivers(dir=\"%s\")", dir_path);
 | 
|---|
| 173 | 
 | 
|---|
| 174 |         int drv_cnt = 0;
 | 
|---|
| 175 |         DIR *dir = NULL;
 | 
|---|
| 176 |         struct dirent *diren;
 | 
|---|
| 177 | 
 | 
|---|
| 178 |         dir = opendir(dir_path);
 | 
|---|
| 179 | 
 | 
|---|
| 180 |         if (dir != NULL) {
 | 
|---|
| 181 |                 driver_t *drv = create_driver();
 | 
|---|
| 182 |                 while ((diren = readdir(dir))) {
 | 
|---|
| 183 |                         if (get_driver_info(dir_path, diren->d_name, drv)) {
 | 
|---|
| 184 |                                 add_driver(drivers_list, drv);
 | 
|---|
| 185 |                                 drv_cnt++;
 | 
|---|
| 186 |                                 drv = create_driver();
 | 
|---|
| 187 |                         }
 | 
|---|
| 188 |                 }
 | 
|---|
| 189 |                 delete_driver(drv);
 | 
|---|
| 190 |                 closedir(dir);
 | 
|---|
| 191 |         }
 | 
|---|
| 192 | 
 | 
|---|
| 193 |         return drv_cnt;
 | 
|---|
| 194 | }
 | 
|---|
| 195 | 
 | 
|---|
| 196 | /** Lookup the best matching driver for the specified device in the list of
 | 
|---|
| 197 |  * drivers.
 | 
|---|
| 198 |  *
 | 
|---|
| 199 |  * A match between a device and a driver is found if one of the driver's match
 | 
|---|
| 200 |  * ids match one of the device's match ids. The score of the match is the
 | 
|---|
| 201 |  * product of the driver's and device's score associated with the matching id.
 | 
|---|
| 202 |  * The best matching driver for a device is the driver with the highest score
 | 
|---|
| 203 |  * of the match between the device and the driver.
 | 
|---|
| 204 |  *
 | 
|---|
| 205 |  * @param drivers_list  The list of drivers, where we look for the driver
 | 
|---|
| 206 |  *                      suitable for handling the device.
 | 
|---|
| 207 |  * @param node          The device node structure of the device.
 | 
|---|
| 208 |  * @return              The best matching driver or NULL if no matching driver
 | 
|---|
| 209 |  *                      is found.
 | 
|---|
| 210 |  */
 | 
|---|
| 211 | driver_t *find_best_match_driver(driver_list_t *drivers_list, dev_node_t *node)
 | 
|---|
| 212 | {
 | 
|---|
| 213 |         driver_t *best_drv = NULL;
 | 
|---|
| 214 |         int best_score = 0, score = 0;
 | 
|---|
| 215 | 
 | 
|---|
| 216 |         fibril_mutex_lock(&drivers_list->drivers_mutex);
 | 
|---|
| 217 | 
 | 
|---|
| 218 |         list_foreach(drivers_list->drivers, drivers, driver_t, drv) {
 | 
|---|
| 219 |                 score = get_match_score(drv, node);
 | 
|---|
| 220 |                 if (score > best_score) {
 | 
|---|
| 221 |                         best_score = score;
 | 
|---|
| 222 |                         best_drv = drv;
 | 
|---|
| 223 |                 }
 | 
|---|
| 224 |         }
 | 
|---|
| 225 | 
 | 
|---|
| 226 |         fibril_mutex_unlock(&drivers_list->drivers_mutex);
 | 
|---|
| 227 | 
 | 
|---|
| 228 |         return best_drv;
 | 
|---|
| 229 | }
 | 
|---|
| 230 | 
 | 
|---|
| 231 | /** Assign a driver to a device.
 | 
|---|
| 232 |  *
 | 
|---|
| 233 |  * @param tree          Device tree
 | 
|---|
| 234 |  * @param node          The device's node in the device tree.
 | 
|---|
| 235 |  * @param drv           The driver.
 | 
|---|
| 236 |  */
 | 
|---|
| 237 | void attach_driver(dev_tree_t *tree, dev_node_t *dev, driver_t *drv)
 | 
|---|
| 238 | {
 | 
|---|
| 239 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "attach_driver(dev=\"%s\",drv=\"%s\")",
 | 
|---|
| 240 |             dev->pfun->pathname, drv->name);
 | 
|---|
| 241 | 
 | 
|---|
| 242 |         fibril_mutex_lock(&drv->driver_mutex);
 | 
|---|
| 243 |         fibril_rwlock_write_lock(&tree->rwlock);
 | 
|---|
| 244 | 
 | 
|---|
| 245 |         dev->drv = drv;
 | 
|---|
| 246 |         list_append(&dev->driver_devices, &drv->devices);
 | 
|---|
| 247 | 
 | 
|---|
| 248 |         fibril_rwlock_write_unlock(&tree->rwlock);
 | 
|---|
| 249 |         fibril_mutex_unlock(&drv->driver_mutex);
 | 
|---|
| 250 | }
 | 
|---|
| 251 | 
 | 
|---|
| 252 | /** Detach driver from device.
 | 
|---|
| 253 |  *
 | 
|---|
| 254 |  * @param tree          Device tree
 | 
|---|
| 255 |  * @param node          The device's node in the device tree.
 | 
|---|
| 256 |  * @param drv           The driver.
 | 
|---|
| 257 |  */
 | 
|---|
| 258 | void detach_driver(dev_tree_t *tree, dev_node_t *dev)
 | 
|---|
| 259 | {
 | 
|---|
| 260 |         driver_t *drv = dev->drv;
 | 
|---|
| 261 | 
 | 
|---|
| 262 |         assert(drv != NULL);
 | 
|---|
| 263 | 
 | 
|---|
| 264 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "detach_driver(dev=\"%s\",drv=\"%s\")",
 | 
|---|
| 265 |             dev->pfun->pathname, drv->name);
 | 
|---|
| 266 | 
 | 
|---|
| 267 |         fibril_mutex_lock(&drv->driver_mutex);
 | 
|---|
| 268 |         fibril_rwlock_write_lock(&tree->rwlock);
 | 
|---|
| 269 | 
 | 
|---|
| 270 |         dev->drv = NULL;
 | 
|---|
| 271 |         list_remove(&dev->driver_devices);
 | 
|---|
| 272 | 
 | 
|---|
| 273 |         fibril_rwlock_write_unlock(&tree->rwlock);
 | 
|---|
| 274 |         fibril_mutex_unlock(&drv->driver_mutex);
 | 
|---|
| 275 | }
 | 
|---|
| 276 | 
 | 
|---|
| 277 | /** Start a driver
 | 
|---|
| 278 |  *
 | 
|---|
| 279 |  * @param drv           The driver's structure.
 | 
|---|
| 280 |  * @return              True if the driver's task is successfully spawned, false
 | 
|---|
| 281 |  *                      otherwise.
 | 
|---|
| 282 |  */
 | 
|---|
| 283 | bool start_driver(driver_t *drv)
 | 
|---|
| 284 | {
 | 
|---|
| 285 |         errno_t rc;
 | 
|---|
| 286 | 
 | 
|---|
| 287 |         assert(fibril_mutex_is_locked(&drv->driver_mutex));
 | 
|---|
| 288 | 
 | 
|---|
| 289 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "start_driver(drv=\"%s\")", drv->name);
 | 
|---|
| 290 | 
 | 
|---|
| 291 |         rc = task_spawnl(NULL, NULL, drv->binary_path, drv->binary_path, NULL);
 | 
|---|
| 292 |         if (rc != EOK) {
 | 
|---|
| 293 |                 log_msg(LOG_DEFAULT, LVL_ERROR, "Spawning driver `%s' (%s) failed: %s.",
 | 
|---|
| 294 |                     drv->name, drv->binary_path, str_error(rc));
 | 
|---|
| 295 |                 return false;
 | 
|---|
| 296 |         }
 | 
|---|
| 297 | 
 | 
|---|
| 298 |         drv->state = DRIVER_STARTING;
 | 
|---|
| 299 |         return true;
 | 
|---|
| 300 | }
 | 
|---|
| 301 | 
 | 
|---|
| 302 | /** Stop a driver
 | 
|---|
| 303 |  *
 | 
|---|
| 304 |  * @param drv           The driver's structure.
 | 
|---|
| 305 |  * @return              True if the driver's task is successfully spawned, false
 | 
|---|
| 306 |  *                      otherwise.
 | 
|---|
| 307 |  */
 | 
|---|
| 308 | errno_t stop_driver(driver_t *drv)
 | 
|---|
| 309 | {
 | 
|---|
| 310 |         async_exch_t *exch;
 | 
|---|
| 311 |         errno_t retval;
 | 
|---|
| 312 | 
 | 
|---|
| 313 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "stop_driver(drv=\"%s\")", drv->name);
 | 
|---|
| 314 | 
 | 
|---|
| 315 |         exch = async_exchange_begin(drv->sess);
 | 
|---|
| 316 |         retval = async_req_0_0(exch, DRIVER_STOP);
 | 
|---|
| 317 |         loc_exchange_end(exch);
 | 
|---|
| 318 | 
 | 
|---|
| 319 |         if (retval != EOK)
 | 
|---|
| 320 |                 return retval;
 | 
|---|
| 321 | 
 | 
|---|
| 322 |         drv->state = DRIVER_NOT_STARTED;
 | 
|---|
| 323 |         async_hangup(drv->sess);
 | 
|---|
| 324 |         drv->sess = NULL;
 | 
|---|
| 325 |         return EOK;
 | 
|---|
| 326 | }
 | 
|---|
| 327 | 
 | 
|---|
| 328 | /** Find device driver by handle.
 | 
|---|
| 329 |  *
 | 
|---|
| 330 |  * @param drv_list      The list of device drivers
 | 
|---|
| 331 |  * @param handle        Driver handle
 | 
|---|
| 332 |  * @return              The device driver, if it is in the list,
 | 
|---|
| 333 |  *                      NULL otherwise.
 | 
|---|
| 334 |  */
 | 
|---|
| 335 | driver_t *driver_find(driver_list_t *drv_list, devman_handle_t handle)
 | 
|---|
| 336 | {
 | 
|---|
| 337 |         driver_t *res = NULL;
 | 
|---|
| 338 | 
 | 
|---|
| 339 |         fibril_mutex_lock(&drv_list->drivers_mutex);
 | 
|---|
| 340 | 
 | 
|---|
| 341 |         list_foreach(drv_list->drivers, drivers, driver_t, drv) {
 | 
|---|
| 342 |                 if (drv->handle == handle) {
 | 
|---|
| 343 |                         res = drv;
 | 
|---|
| 344 |                         break;
 | 
|---|
| 345 |                 }
 | 
|---|
| 346 |         }
 | 
|---|
| 347 | 
 | 
|---|
| 348 |         fibril_mutex_unlock(&drv_list->drivers_mutex);
 | 
|---|
| 349 | 
 | 
|---|
| 350 |         return res;
 | 
|---|
| 351 | }
 | 
|---|
| 352 | 
 | 
|---|
| 353 | 
 | 
|---|
| 354 | /** Find device driver by name.
 | 
|---|
| 355 |  *
 | 
|---|
| 356 |  * @param drv_list      The list of device drivers.
 | 
|---|
| 357 |  * @param drv_name      The name of the device driver which is searched.
 | 
|---|
| 358 |  * @return              The device driver of the specified name, if it is in the
 | 
|---|
| 359 |  *                      list, NULL otherwise.
 | 
|---|
| 360 |  */
 | 
|---|
| 361 | driver_t *driver_find_by_name(driver_list_t *drv_list, const char *drv_name)
 | 
|---|
| 362 | {
 | 
|---|
| 363 |         driver_t *res = NULL;
 | 
|---|
| 364 | 
 | 
|---|
| 365 |         fibril_mutex_lock(&drv_list->drivers_mutex);
 | 
|---|
| 366 | 
 | 
|---|
| 367 |         list_foreach(drv_list->drivers, drivers, driver_t, drv) {
 | 
|---|
| 368 |                 if (str_cmp(drv->name, drv_name) == 0) {
 | 
|---|
| 369 |                         res = drv;
 | 
|---|
| 370 |                         break;
 | 
|---|
| 371 |                 }
 | 
|---|
| 372 |         }
 | 
|---|
| 373 | 
 | 
|---|
| 374 |         fibril_mutex_unlock(&drv_list->drivers_mutex);
 | 
|---|
| 375 | 
 | 
|---|
| 376 |         return res;
 | 
|---|
| 377 | }
 | 
|---|
| 378 | 
 | 
|---|
| 379 | /** Notify driver about the devices to which it was assigned.
 | 
|---|
| 380 |  *
 | 
|---|
| 381 |  * @param driver        The driver to which the devices are passed.
 | 
|---|
| 382 |  */
 | 
|---|
| 383 | static void pass_devices_to_driver(driver_t *driver, dev_tree_t *tree)
 | 
|---|
| 384 | {
 | 
|---|
| 385 |         dev_node_t *dev;
 | 
|---|
| 386 |         link_t *link;
 | 
|---|
| 387 | 
 | 
|---|
| 388 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "pass_devices_to_driver(driver=\"%s\")",
 | 
|---|
| 389 |             driver->name);
 | 
|---|
| 390 | 
 | 
|---|
| 391 |         fibril_mutex_lock(&driver->driver_mutex);
 | 
|---|
| 392 | 
 | 
|---|
| 393 |         /*
 | 
|---|
| 394 |          * Go through devices list as long as there is some device
 | 
|---|
| 395 |          * that has not been passed to the driver.
 | 
|---|
| 396 |          */
 | 
|---|
| 397 |         link = driver->devices.head.next;
 | 
|---|
| 398 |         while (link != &driver->devices.head) {
 | 
|---|
| 399 |                 dev = list_get_instance(link, dev_node_t, driver_devices);
 | 
|---|
| 400 |                 fibril_rwlock_write_lock(&tree->rwlock);
 | 
|---|
| 401 | 
 | 
|---|
| 402 |                 if (dev->passed_to_driver) {
 | 
|---|
| 403 |                         fibril_rwlock_write_unlock(&tree->rwlock);
 | 
|---|
| 404 |                         link = link->next;
 | 
|---|
| 405 |                         continue;
 | 
|---|
| 406 |                 }
 | 
|---|
| 407 | 
 | 
|---|
| 408 |                 dev_add_ref(dev);
 | 
|---|
| 409 | 
 | 
|---|
| 410 |                 /*
 | 
|---|
| 411 |                  * Unlock to avoid deadlock when adding device
 | 
|---|
| 412 |                  * handled by itself.
 | 
|---|
| 413 |                  */
 | 
|---|
| 414 |                 fibril_mutex_unlock(&driver->driver_mutex);
 | 
|---|
| 415 |                 fibril_rwlock_write_unlock(&tree->rwlock);
 | 
|---|
| 416 | 
 | 
|---|
| 417 |                 add_device(driver, dev, tree);
 | 
|---|
| 418 | 
 | 
|---|
| 419 |                 dev_del_ref(dev);
 | 
|---|
| 420 | 
 | 
|---|
| 421 |                 /*
 | 
|---|
| 422 |                  * Lock again as we will work with driver's
 | 
|---|
| 423 |                  * structure.
 | 
|---|
| 424 |                  */
 | 
|---|
| 425 |                 fibril_mutex_lock(&driver->driver_mutex);
 | 
|---|
| 426 | 
 | 
|---|
| 427 |                 /*
 | 
|---|
| 428 |                  * Restart the cycle to go through all devices again.
 | 
|---|
| 429 |                  */
 | 
|---|
| 430 |                 link = driver->devices.head.next;
 | 
|---|
| 431 |         }
 | 
|---|
| 432 | 
 | 
|---|
| 433 |         /*
 | 
|---|
| 434 |          * Once we passed all devices to the driver, we need to mark the
 | 
|---|
| 435 |          * driver as running.
 | 
|---|
| 436 |          * It is vital to do it here and inside critical section.
 | 
|---|
| 437 |          *
 | 
|---|
| 438 |          * If we would change the state earlier, other devices added to
 | 
|---|
| 439 |          * the driver would be added to the device list and started
 | 
|---|
| 440 |          * immediately and possibly started here as well.
 | 
|---|
| 441 |          */
 | 
|---|
| 442 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "Driver `%s' enters running state.", driver->name);
 | 
|---|
| 443 |         driver->state = DRIVER_RUNNING;
 | 
|---|
| 444 | 
 | 
|---|
| 445 |         fibril_mutex_unlock(&driver->driver_mutex);
 | 
|---|
| 446 | }
 | 
|---|
| 447 | 
 | 
|---|
| 448 | /** Finish the initialization of a driver after it has succesfully started
 | 
|---|
| 449 |  * and after it has registered itself by the device manager.
 | 
|---|
| 450 |  *
 | 
|---|
| 451 |  * Pass devices formerly matched to the driver to the driver and remember the
 | 
|---|
| 452 |  * driver is running and fully functional now.
 | 
|---|
| 453 |  *
 | 
|---|
| 454 |  * @param driver        The driver which registered itself as running by the
 | 
|---|
| 455 |  *                      device manager.
 | 
|---|
| 456 |  */
 | 
|---|
| 457 | void initialize_running_driver(driver_t *driver, dev_tree_t *tree)
 | 
|---|
| 458 | {
 | 
|---|
| 459 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "initialize_running_driver(driver=\"%s\")",
 | 
|---|
| 460 |             driver->name);
 | 
|---|
| 461 | 
 | 
|---|
| 462 |         /*
 | 
|---|
| 463 |          * Pass devices which have been already assigned to the driver to the
 | 
|---|
| 464 |          * driver.
 | 
|---|
| 465 |          */
 | 
|---|
| 466 |         pass_devices_to_driver(driver, tree);
 | 
|---|
| 467 | }
 | 
|---|
| 468 | 
 | 
|---|
| 469 | /** Initialize device driver structure.
 | 
|---|
| 470 |  *
 | 
|---|
| 471 |  * @param drv           The device driver structure.
 | 
|---|
| 472 |  */
 | 
|---|
| 473 | void init_driver(driver_t *drv)
 | 
|---|
| 474 | {
 | 
|---|
| 475 |         assert(drv != NULL);
 | 
|---|
| 476 | 
 | 
|---|
| 477 |         memset(drv, 0, sizeof(driver_t));
 | 
|---|
| 478 |         list_initialize(&drv->match_ids.ids);
 | 
|---|
| 479 |         list_initialize(&drv->devices);
 | 
|---|
| 480 |         fibril_mutex_initialize(&drv->driver_mutex);
 | 
|---|
| 481 |         drv->sess = NULL;
 | 
|---|
| 482 | }
 | 
|---|
| 483 | 
 | 
|---|
| 484 | /** Device driver structure clean-up.
 | 
|---|
| 485 |  *
 | 
|---|
| 486 |  * @param drv           The device driver structure.
 | 
|---|
| 487 |  */
 | 
|---|
| 488 | void clean_driver(driver_t *drv)
 | 
|---|
| 489 | {
 | 
|---|
| 490 |         assert(drv != NULL);
 | 
|---|
| 491 | 
 | 
|---|
| 492 |         free(drv->name);
 | 
|---|
| 493 |         free(drv->binary_path);
 | 
|---|
| 494 | 
 | 
|---|
| 495 |         clean_match_ids(&drv->match_ids);
 | 
|---|
| 496 | 
 | 
|---|
| 497 |         init_driver(drv);
 | 
|---|
| 498 | }
 | 
|---|
| 499 | 
 | 
|---|
| 500 | /** Delete device driver structure.
 | 
|---|
| 501 |  *
 | 
|---|
| 502 |  * @param drv           The device driver structure.
 | 
|---|
| 503 |  */
 | 
|---|
| 504 | void delete_driver(driver_t *drv)
 | 
|---|
| 505 | {
 | 
|---|
| 506 |         assert(drv != NULL);
 | 
|---|
| 507 | 
 | 
|---|
| 508 |         clean_driver(drv);
 | 
|---|
| 509 |         free(drv);
 | 
|---|
| 510 | }
 | 
|---|
| 511 | 
 | 
|---|
| 512 | /** Find suitable driver for a device and assign the driver to it.
 | 
|---|
| 513 |  *
 | 
|---|
| 514 |  * @param node          The device node of the device in the device tree.
 | 
|---|
| 515 |  * @param drivers_list  The list of available drivers.
 | 
|---|
| 516 |  * @return              True if the suitable driver is found and
 | 
|---|
| 517 |  *                      successfully assigned to the device, false otherwise.
 | 
|---|
| 518 |  */
 | 
|---|
| 519 | bool assign_driver(dev_node_t *dev, driver_list_t *drivers_list,
 | 
|---|
| 520 |     dev_tree_t *tree)
 | 
|---|
| 521 | {
 | 
|---|
| 522 |         assert(dev != NULL);
 | 
|---|
| 523 |         assert(drivers_list != NULL);
 | 
|---|
| 524 |         assert(tree != NULL);
 | 
|---|
| 525 | 
 | 
|---|
| 526 |         /*
 | 
|---|
| 527 |          * Find the driver which is the most suitable for handling this device.
 | 
|---|
| 528 |          */
 | 
|---|
| 529 |         driver_t *drv = find_best_match_driver(drivers_list, dev);
 | 
|---|
| 530 |         if (drv == NULL) {
 | 
|---|
| 531 |                 log_msg(LOG_DEFAULT, LVL_ERROR, "No driver found for device `%s'.",
 | 
|---|
| 532 |                     dev->pfun->pathname);
 | 
|---|
| 533 |                 return false;
 | 
|---|
| 534 |         }
 | 
|---|
| 535 | 
 | 
|---|
| 536 |         /* Attach the driver to the device. */
 | 
|---|
| 537 |         attach_driver(tree, dev, drv);
 | 
|---|
| 538 | 
 | 
|---|
| 539 |         fibril_mutex_lock(&drv->driver_mutex);
 | 
|---|
| 540 |         if (drv->state == DRIVER_NOT_STARTED) {
 | 
|---|
| 541 |                 /* Start the driver. */
 | 
|---|
| 542 |                 start_driver(drv);
 | 
|---|
| 543 |         }
 | 
|---|
| 544 |         bool is_running = drv->state == DRIVER_RUNNING;
 | 
|---|
| 545 |         fibril_mutex_unlock(&drv->driver_mutex);
 | 
|---|
| 546 | 
 | 
|---|
| 547 |         /* Notify the driver about the new device. */
 | 
|---|
| 548 |         if (is_running)
 | 
|---|
| 549 |                 add_device(drv, dev, tree);
 | 
|---|
| 550 | 
 | 
|---|
| 551 |         fibril_mutex_lock(&drv->driver_mutex);
 | 
|---|
| 552 |         fibril_mutex_unlock(&drv->driver_mutex);
 | 
|---|
| 553 | 
 | 
|---|
| 554 |         fibril_rwlock_write_lock(&tree->rwlock);
 | 
|---|
| 555 |         if (dev->pfun != NULL) {
 | 
|---|
| 556 |                 dev->pfun->state = FUN_ON_LINE;
 | 
|---|
| 557 |         }
 | 
|---|
| 558 |         fibril_rwlock_write_unlock(&tree->rwlock);
 | 
|---|
| 559 |         return true;
 | 
|---|
| 560 | }
 | 
|---|
| 561 | 
 | 
|---|
| 562 | /** Pass a device to running driver.
 | 
|---|
| 563 |  *
 | 
|---|
| 564 |  * @param drv           The driver's structure.
 | 
|---|
| 565 |  * @param node          The device's node in the device tree.
 | 
|---|
| 566 |  */
 | 
|---|
| 567 | void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
 | 
|---|
| 568 | {
 | 
|---|
| 569 |         /*
 | 
|---|
| 570 |          * We do not expect to have driver's mutex locked as we do not
 | 
|---|
| 571 |          * access any structures that would affect driver_t.
 | 
|---|
| 572 |          */
 | 
|---|
| 573 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "add_device(drv=\"%s\", dev=\"%s\")",
 | 
|---|
| 574 |             drv->name, dev->pfun->name);
 | 
|---|
| 575 | 
 | 
|---|
| 576 |         /* Send the device to the driver. */
 | 
|---|
| 577 |         devman_handle_t parent_handle;
 | 
|---|
| 578 |         if (dev->pfun) {
 | 
|---|
| 579 |                 parent_handle = dev->pfun->handle;
 | 
|---|
| 580 |         } else {
 | 
|---|
| 581 |                 parent_handle = 0;
 | 
|---|
| 582 |         }
 | 
|---|
| 583 | 
 | 
|---|
| 584 |         async_exch_t *exch = async_exchange_begin(drv->sess);
 | 
|---|
| 585 | 
 | 
|---|
| 586 |         ipc_call_t answer;
 | 
|---|
| 587 |         aid_t req = async_send_2(exch, DRIVER_DEV_ADD, dev->handle,
 | 
|---|
| 588 |             parent_handle, &answer);
 | 
|---|
| 589 | 
 | 
|---|
| 590 |         /* Send the device name to the driver. */
 | 
|---|
| 591 |         errno_t rc = async_data_write_start(exch, dev->pfun->name,
 | 
|---|
| 592 |             str_size(dev->pfun->name) + 1);
 | 
|---|
| 593 | 
 | 
|---|
| 594 |         async_exchange_end(exch);
 | 
|---|
| 595 | 
 | 
|---|
| 596 |         if (rc != EOK) {
 | 
|---|
| 597 |                 async_forget(req);
 | 
|---|
| 598 |         } else {
 | 
|---|
| 599 |                 /* Wait for answer from the driver. */
 | 
|---|
| 600 |                 async_wait_for(req, &rc);
 | 
|---|
| 601 |         }
 | 
|---|
| 602 | 
 | 
|---|
| 603 |         switch (rc) {
 | 
|---|
| 604 |         case EOK:
 | 
|---|
| 605 |                 dev->state = DEVICE_USABLE;
 | 
|---|
| 606 |                 break;
 | 
|---|
| 607 |         case ENOENT:
 | 
|---|
| 608 |                 dev->state = DEVICE_NOT_PRESENT;
 | 
|---|
| 609 |                 break;
 | 
|---|
| 610 |         default:
 | 
|---|
| 611 |                 dev->state = DEVICE_INVALID;
 | 
|---|
| 612 |                 break;
 | 
|---|
| 613 |         }
 | 
|---|
| 614 | 
 | 
|---|
| 615 |         dev->passed_to_driver = true;
 | 
|---|
| 616 | }
 | 
|---|
| 617 | 
 | 
|---|
| 618 | errno_t driver_dev_remove(dev_tree_t *tree, dev_node_t *dev)
 | 
|---|
| 619 | {
 | 
|---|
| 620 |         async_exch_t *exch;
 | 
|---|
| 621 |         errno_t retval;
 | 
|---|
| 622 |         driver_t *drv;
 | 
|---|
| 623 |         devman_handle_t handle;
 | 
|---|
| 624 | 
 | 
|---|
| 625 |         assert(dev != NULL);
 | 
|---|
| 626 | 
 | 
|---|
| 627 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_remove(%p)", dev);
 | 
|---|
| 628 | 
 | 
|---|
| 629 |         fibril_rwlock_read_lock(&tree->rwlock);
 | 
|---|
| 630 |         drv = dev->drv;
 | 
|---|
| 631 |         handle = dev->handle;
 | 
|---|
| 632 |         fibril_rwlock_read_unlock(&tree->rwlock);
 | 
|---|
| 633 | 
 | 
|---|
| 634 |         exch = async_exchange_begin(drv->sess);
 | 
|---|
| 635 |         retval = async_req_1_0(exch, DRIVER_DEV_REMOVE, handle);
 | 
|---|
| 636 |         async_exchange_end(exch);
 | 
|---|
| 637 | 
 | 
|---|
| 638 |         return retval;
 | 
|---|
| 639 | }
 | 
|---|
| 640 | 
 | 
|---|
| 641 | errno_t driver_dev_gone(dev_tree_t *tree, dev_node_t *dev)
 | 
|---|
| 642 | {
 | 
|---|
| 643 |         async_exch_t *exch;
 | 
|---|
| 644 |         errno_t retval;
 | 
|---|
| 645 |         driver_t *drv;
 | 
|---|
| 646 |         devman_handle_t handle;
 | 
|---|
| 647 | 
 | 
|---|
| 648 |         assert(dev != NULL);
 | 
|---|
| 649 | 
 | 
|---|
| 650 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_dev_gone(%p)", dev);
 | 
|---|
| 651 | 
 | 
|---|
| 652 |         fibril_rwlock_read_lock(&tree->rwlock);
 | 
|---|
| 653 |         drv = dev->drv;
 | 
|---|
| 654 |         handle = dev->handle;
 | 
|---|
| 655 |         fibril_rwlock_read_unlock(&tree->rwlock);
 | 
|---|
| 656 | 
 | 
|---|
| 657 |         exch = async_exchange_begin(drv->sess);
 | 
|---|
| 658 |         retval = async_req_1_0(exch, DRIVER_DEV_GONE, handle);
 | 
|---|
| 659 |         async_exchange_end(exch);
 | 
|---|
| 660 | 
 | 
|---|
| 661 |         return retval;
 | 
|---|
| 662 | }
 | 
|---|
| 663 | 
 | 
|---|
| 664 | errno_t driver_fun_online(dev_tree_t *tree, fun_node_t *fun)
 | 
|---|
| 665 | {
 | 
|---|
| 666 |         async_exch_t *exch;
 | 
|---|
| 667 |         errno_t retval;
 | 
|---|
| 668 |         driver_t *drv;
 | 
|---|
| 669 |         devman_handle_t handle;
 | 
|---|
| 670 | 
 | 
|---|
| 671 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_online(%p)", fun);
 | 
|---|
| 672 | 
 | 
|---|
| 673 |         fibril_rwlock_read_lock(&tree->rwlock);
 | 
|---|
| 674 | 
 | 
|---|
| 675 |         if (fun->dev == NULL) {
 | 
|---|
| 676 |                 /* XXX root function? */
 | 
|---|
| 677 |                 fibril_rwlock_read_unlock(&tree->rwlock);
 | 
|---|
| 678 |                 return EINVAL;
 | 
|---|
| 679 |         }
 | 
|---|
| 680 | 
 | 
|---|
| 681 |         drv = fun->dev->drv;
 | 
|---|
| 682 |         handle = fun->handle;
 | 
|---|
| 683 |         fibril_rwlock_read_unlock(&tree->rwlock);
 | 
|---|
| 684 | 
 | 
|---|
| 685 |         exch = async_exchange_begin(drv->sess);
 | 
|---|
| 686 |         retval = async_req_1_0(exch, DRIVER_FUN_ONLINE, handle);
 | 
|---|
| 687 |         loc_exchange_end(exch);
 | 
|---|
| 688 | 
 | 
|---|
| 689 |         return retval;
 | 
|---|
| 690 | }
 | 
|---|
| 691 | 
 | 
|---|
| 692 | errno_t driver_fun_offline(dev_tree_t *tree, fun_node_t *fun)
 | 
|---|
| 693 | {
 | 
|---|
| 694 |         async_exch_t *exch;
 | 
|---|
| 695 |         errno_t retval;
 | 
|---|
| 696 |         driver_t *drv;
 | 
|---|
| 697 |         devman_handle_t handle;
 | 
|---|
| 698 | 
 | 
|---|
| 699 |         log_msg(LOG_DEFAULT, LVL_DEBUG, "driver_fun_offline(%p)", fun);
 | 
|---|
| 700 | 
 | 
|---|
| 701 |         fibril_rwlock_read_lock(&tree->rwlock);
 | 
|---|
| 702 |         if (fun->dev == NULL) {
 | 
|---|
| 703 |                 /* XXX root function? */
 | 
|---|
| 704 |                 fibril_rwlock_read_unlock(&tree->rwlock);
 | 
|---|
| 705 |                 return EINVAL;
 | 
|---|
| 706 |         }
 | 
|---|
| 707 | 
 | 
|---|
| 708 |         drv = fun->dev->drv;
 | 
|---|
| 709 |         handle = fun->handle;
 | 
|---|
| 710 |         fibril_rwlock_read_unlock(&tree->rwlock);
 | 
|---|
| 711 | 
 | 
|---|
| 712 |         exch = async_exchange_begin(drv->sess);
 | 
|---|
| 713 |         retval = async_req_1_0(exch, DRIVER_FUN_OFFLINE, handle);
 | 
|---|
| 714 |         loc_exchange_end(exch);
 | 
|---|
| 715 | 
 | 
|---|
| 716 |         return retval;
 | 
|---|
| 717 | 
 | 
|---|
| 718 | }
 | 
|---|
| 719 | 
 | 
|---|
| 720 | /** Get list of registered drivers. */
 | 
|---|
| 721 | errno_t driver_get_list(driver_list_t *driver_list, devman_handle_t *hdl_buf,
 | 
|---|
| 722 |     size_t buf_size, size_t *act_size)
 | 
|---|
| 723 | {
 | 
|---|
| 724 |         size_t act_cnt;
 | 
|---|
| 725 |         size_t buf_cnt;
 | 
|---|
| 726 | 
 | 
|---|
| 727 |         fibril_mutex_lock(&driver_list->drivers_mutex);
 | 
|---|
| 728 | 
 | 
|---|
| 729 |         buf_cnt = buf_size / sizeof(devman_handle_t);
 | 
|---|
| 730 | 
 | 
|---|
| 731 |         act_cnt = list_count(&driver_list->drivers);
 | 
|---|
| 732 |         *act_size = act_cnt * sizeof(devman_handle_t);
 | 
|---|
| 733 | 
 | 
|---|
| 734 |         if (buf_size % sizeof(devman_handle_t) != 0) {
 | 
|---|
| 735 |                 fibril_mutex_unlock(&driver_list->drivers_mutex);
 | 
|---|
| 736 |                 return EINVAL;
 | 
|---|
| 737 |         }
 | 
|---|
| 738 | 
 | 
|---|
| 739 |         size_t pos = 0;
 | 
|---|
| 740 |         list_foreach(driver_list->drivers, drivers, driver_t, drv) {
 | 
|---|
| 741 |                 if (pos < buf_cnt) {
 | 
|---|
| 742 |                         hdl_buf[pos] = drv->handle;
 | 
|---|
| 743 |                 }
 | 
|---|
| 744 | 
 | 
|---|
| 745 |                 pos++;
 | 
|---|
| 746 |         }
 | 
|---|
| 747 | 
 | 
|---|
| 748 |         fibril_mutex_unlock(&driver_list->drivers_mutex);
 | 
|---|
| 749 |         return EOK;
 | 
|---|
| 750 | }
 | 
|---|
| 751 | 
 | 
|---|
| 752 | /** Get list of device functions. */
 | 
|---|
| 753 | errno_t driver_get_devices(driver_t *driver, devman_handle_t *hdl_buf,
 | 
|---|
| 754 |     size_t buf_size, size_t *act_size)
 | 
|---|
| 755 | {
 | 
|---|
| 756 |         size_t act_cnt;
 | 
|---|
| 757 |         size_t buf_cnt;
 | 
|---|
| 758 | 
 | 
|---|
| 759 |         fibril_mutex_lock(&driver->driver_mutex);
 | 
|---|
| 760 | 
 | 
|---|
| 761 |         buf_cnt = buf_size / sizeof(devman_handle_t);
 | 
|---|
| 762 | 
 | 
|---|
| 763 |         act_cnt = list_count(&driver->devices);
 | 
|---|
| 764 |         *act_size = act_cnt * sizeof(devman_handle_t);
 | 
|---|
| 765 | 
 | 
|---|
| 766 |         if (buf_size % sizeof(devman_handle_t) != 0) {
 | 
|---|
| 767 |                 fibril_mutex_unlock(&driver->driver_mutex);
 | 
|---|
| 768 |                 return EINVAL;
 | 
|---|
| 769 |         }
 | 
|---|
| 770 | 
 | 
|---|
| 771 |         size_t pos = 0;
 | 
|---|
| 772 |         list_foreach(driver->devices, driver_devices, dev_node_t, dev) {
 | 
|---|
| 773 |                 if (pos < buf_cnt) {
 | 
|---|
| 774 |                         hdl_buf[pos] = dev->handle;
 | 
|---|
| 775 |                 }
 | 
|---|
| 776 | 
 | 
|---|
| 777 |                 pos++;
 | 
|---|
| 778 |         }
 | 
|---|
| 779 | 
 | 
|---|
| 780 |         fibril_mutex_unlock(&driver->driver_mutex);
 | 
|---|
| 781 |         return EOK;
 | 
|---|
| 782 | }
 | 
|---|
| 783 | 
 | 
|---|
| 784 | /** @}
 | 
|---|
| 785 |  */
 | 
|---|