source: mainline/uspace/lib/drv/generic/driver.c@ 3951046

Last change on this file since 3951046 was 8300c72, checked in by Jiri Svoboda <jiri@…>, 4 months ago

Quiesce devices before proceeding with shutdown.

Only implemented for e1k, uhci and xhci.

  • Property mode set to 100644
File size: 24.3 KB
RevLine 
[c16cf62]1/*
[832cbe7]2 * Copyright (c) 2025 Jiri Svoboda
[c16cf62]3 * Copyright (c) 2010 Lenka Trochtova
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/**
[4122410]31 * @addtogroup libdrv
[c16cf62]32 * @{
33 */
34
35/** @file
36 */
[52b7b1bb]37
[c16cf62]38#include <assert.h>
39#include <ipc/services.h>
40#include <ipc/ns.h>
41#include <async.h>
42#include <stdio.h>
43#include <errno.h>
[3e6a98c5]44#include <stdbool.h>
[c16cf62]45#include <fibril_synch.h>
46#include <stdlib.h>
[c47e1a8]47#include <str.h>
[3ad7b1c]48#include <str_error.h>
[c16cf62]49#include <ctype.h>
[7e752b2]50#include <inttypes.h>
[af6b5157]51#include <devman.h>
[c16cf62]52
[5fdd7c3]53#include "dev_iface.h"
[af6b5157]54#include "ddf/driver.h"
55#include "ddf/interrupt.h"
[56fd7cf]56#include "private/driver.h"
[c16cf62]57
[36f2b3e]58/** Driver structure */
[0c322fa]59static const driver_t *driver;
[7f8b581]60
[36f2b3e]61/** Devices */
[1a5b252]62LIST_INITIALIZE(devices);
63FIBRIL_MUTEX_INITIALIZE(devices_mutex);
64
65/** Functions */
[8b1e15ac]66LIST_INITIALIZE(functions);
67FIBRIL_MUTEX_INITIALIZE(functions_mutex);
[084ff99]68
[81685dd9]69FIBRIL_RWLOCK_INITIALIZE(stopping_lock);
70static bool stopping = false;
71
[83a2f43]72static ddf_dev_t *create_device(void);
73static void delete_device(ddf_dev_t *);
[0f0f8bc]74static void dev_add_ref(ddf_dev_t *);
75static void dev_del_ref(ddf_dev_t *);
76static void fun_add_ref(ddf_fun_t *);
77static void fun_del_ref(ddf_fun_t *);
[af6b5157]78static remote_handler_t *function_get_default_handler(ddf_fun_t *);
79static void *function_get_ops(ddf_fun_t *, dev_inferface_idx_t);
[7f8b581]80
[83a2f43]81static void add_to_functions_list(ddf_fun_t *fun)
[9a66bc2e]82{
[8b1e15ac]83 fibril_mutex_lock(&functions_mutex);
84 list_append(&fun->link, &functions);
85 fibril_mutex_unlock(&functions_mutex);
[9a66bc2e]86}
87
[83a2f43]88static void remove_from_functions_list(ddf_fun_t *fun)
[9a66bc2e]89{
[8b1e15ac]90 fibril_mutex_lock(&functions_mutex);
91 list_remove(&fun->link);
92 fibril_mutex_unlock(&functions_mutex);
[9a66bc2e]93}
94
[1a5b252]95static ddf_dev_t *driver_get_device(devman_handle_t handle)
96{
97 assert(fibril_mutex_is_locked(&devices_mutex));
[a35b458]98
[feeac0d]99 list_foreach(devices, link, ddf_dev_t, dev) {
[1a5b252]100 if (dev->handle == handle)
101 return dev;
102 }
[a35b458]103
[1a5b252]104 return NULL;
105}
106
107static ddf_fun_t *driver_get_function(devman_handle_t handle)
[52b7b1bb]108{
[1a5b252]109 assert(fibril_mutex_is_locked(&functions_mutex));
[a35b458]110
[feeac0d]111 list_foreach(functions, link, ddf_fun_t, fun) {
[1a5b252]112 if (fun->handle == handle)
[8b1e15ac]113 return fun;
[a1769ee]114 }
[a35b458]115
[a1769ee]116 return NULL;
117}
118
[984a9ba]119static void driver_dev_add(ipc_call_t *icall)
[084ff99]120{
[fafb8e5]121 devman_handle_t dev_handle = ipc_get_arg1(icall);
122 devman_handle_t parent_fun_handle = ipc_get_arg2(icall);
[a35b458]123
[77ad86c]124 char *dev_name = NULL;
[5a6cc679]125 errno_t rc = async_data_write_accept((void **) &dev_name, true, 0, 0, 0, 0);
[f9b2cb4c]126 if (rc != EOK) {
[984a9ba]127 async_answer_0(icall, rc);
[f9b2cb4c]128 return;
129 }
[a35b458]130
[81685dd9]131 fibril_rwlock_read_lock(&stopping_lock);
132
133 if (stopping) {
134 fibril_rwlock_read_unlock(&stopping_lock);
[984a9ba]135 async_answer_0(icall, EIO);
[81685dd9]136 return;
137 }
[a35b458]138
[562a48b]139 ddf_dev_t *dev = create_device();
[479cc46]140 if (!dev) {
[81685dd9]141 fibril_rwlock_read_unlock(&stopping_lock);
[479cc46]142 free(dev_name);
[984a9ba]143 async_answer_0(icall, ENOMEM);
[479cc46]144 return;
145 }
[a35b458]146
[562a48b]147 dev->handle = dev_handle;
[df747b9c]148 dev->name = dev_name;
[a35b458]149
[8b1e15ac]150 /*
151 * Currently not used, parent fun handle is stored in context
152 * of the connection to the parent device driver.
153 */
154 (void) parent_fun_handle;
[a35b458]155
[5a6cc679]156 errno_t res = driver->driver_ops->dev_add(dev);
[a35b458]157
[0f0f8bc]158 if (res != EOK) {
[81685dd9]159 fibril_rwlock_read_unlock(&stopping_lock);
[0f0f8bc]160 dev_del_ref(dev);
[984a9ba]161 async_answer_0(icall, res);
[0f0f8bc]162 return;
163 }
[a35b458]164
[1a5b252]165 fibril_mutex_lock(&devices_mutex);
166 list_append(&dev->link, &devices);
167 fibril_mutex_unlock(&devices_mutex);
[81685dd9]168 fibril_rwlock_read_unlock(&stopping_lock);
[a35b458]169
[984a9ba]170 async_answer_0(icall, res);
[084ff99]171}
[c16cf62]172
[984a9ba]173static void driver_dev_remove(ipc_call_t *icall)
[1a5b252]174{
[fafb8e5]175 devman_handle_t devh = ipc_get_arg1(icall);
[a35b458]176
[1a5b252]177 fibril_mutex_lock(&devices_mutex);
[77ad86c]178 ddf_dev_t *dev = driver_get_device(devh);
[f278930]179 if (dev != NULL)
180 dev_add_ref(dev);
[1a5b252]181 fibril_mutex_unlock(&devices_mutex);
[a35b458]182
[1a5b252]183 if (dev == NULL) {
[984a9ba]184 async_answer_0(icall, ENOENT);
[1a5b252]185 return;
186 }
[a35b458]187
[5a6cc679]188 errno_t rc;
[a35b458]189
[1a5b252]190 if (driver->driver_ops->dev_remove != NULL)
191 rc = driver->driver_ops->dev_remove(dev);
192 else
193 rc = ENOTSUP;
[a35b458]194
[702729e]195 if (rc == EOK) {
196 fibril_mutex_lock(&devices_mutex);
197 list_remove(&dev->link);
198 fibril_mutex_unlock(&devices_mutex);
[0f0f8bc]199 dev_del_ref(dev);
[702729e]200 }
[a35b458]201
[203b80c]202 dev_del_ref(dev);
[984a9ba]203 async_answer_0(icall, rc);
[1a5b252]204}
205
[984a9ba]206static void driver_dev_gone(ipc_call_t *icall)
[80a96d2]207{
[fafb8e5]208 devman_handle_t devh = ipc_get_arg1(icall);
[a35b458]209
[80a96d2]210 fibril_mutex_lock(&devices_mutex);
[77ad86c]211 ddf_dev_t *dev = driver_get_device(devh);
[80a96d2]212 if (dev != NULL)
213 dev_add_ref(dev);
214 fibril_mutex_unlock(&devices_mutex);
[a35b458]215
[80a96d2]216 if (dev == NULL) {
[984a9ba]217 async_answer_0(icall, ENOENT);
[80a96d2]218 return;
219 }
[a35b458]220
[5a6cc679]221 errno_t rc;
[a35b458]222
[80a96d2]223 if (driver->driver_ops->dev_gone != NULL)
224 rc = driver->driver_ops->dev_gone(dev);
225 else
226 rc = ENOTSUP;
[a35b458]227
[702729e]228 if (rc == EOK) {
229 fibril_mutex_lock(&devices_mutex);
230 list_remove(&dev->link);
231 fibril_mutex_unlock(&devices_mutex);
[80a96d2]232 dev_del_ref(dev);
[702729e]233 }
[a35b458]234
[203b80c]235 dev_del_ref(dev);
[984a9ba]236 async_answer_0(icall, rc);
[80a96d2]237}
238
[8300c72]239static void driver_dev_quiesce(ipc_call_t *icall)
240{
241 devman_handle_t devh = ipc_get_arg1(icall);
242 ddf_fun_t *fun;
243 link_t *link;
244
245 fibril_mutex_lock(&devices_mutex);
246 ddf_dev_t *dev = driver_get_device(devh);
247 if (dev != NULL)
248 dev_add_ref(dev);
249 fibril_mutex_unlock(&devices_mutex);
250
251 if (dev == NULL) {
252 async_answer_0(icall, ENOENT);
253 return;
254 }
255
256 errno_t rc;
257
258 if (driver->driver_ops->dev_quiesce != NULL) {
259 rc = driver->driver_ops->dev_quiesce(dev);
260 } else {
261 /*
262 * If the driver does not implement quiesce, we will
263 * simply request all subordinate functions to quiesce.
264 */
265 fibril_mutex_lock(&functions_mutex);
266 link = list_first(&functions);
267 while (link != NULL) {
268 fun = list_get_instance(link, ddf_fun_t, link);
269 if (fun->dev == dev)
270 ddf_fun_quiesce(fun);
271 link = list_next(link, &functions);
272 }
273 fibril_mutex_unlock(&functions_mutex);
274 rc = EOK;
275 }
276
277 dev_del_ref(dev);
278 async_answer_0(icall, rc);
279}
280
[984a9ba]281static void driver_fun_online(ipc_call_t *icall)
[1a5b252]282{
[fafb8e5]283 devman_handle_t funh = ipc_get_arg1(icall);
[a35b458]284
[0f0f8bc]285 /*
[4d94002d]286 * Look the function up. Bump reference count so that
[0f0f8bc]287 * the function continues to exist until we return
288 * from the driver.
289 */
[1a5b252]290 fibril_mutex_lock(&functions_mutex);
[a35b458]291
[77ad86c]292 ddf_fun_t *fun = driver_get_function(funh);
[0f0f8bc]293 if (fun != NULL)
294 fun_add_ref(fun);
[a35b458]295
[1a5b252]296 fibril_mutex_unlock(&functions_mutex);
[a35b458]297
[1a5b252]298 if (fun == NULL) {
[984a9ba]299 async_answer_0(icall, ENOENT);
[1a5b252]300 return;
301 }
[a35b458]302
[0f0f8bc]303 /* Call driver entry point */
[5a6cc679]304 errno_t rc;
[a35b458]305
[1a5b252]306 if (driver->driver_ops->fun_online != NULL)
307 rc = driver->driver_ops->fun_online(fun);
308 else
309 rc = ENOTSUP;
[a35b458]310
[0f0f8bc]311 fun_del_ref(fun);
[a35b458]312
[984a9ba]313 async_answer_0(icall, rc);
[1a5b252]314}
315
[984a9ba]316static void driver_fun_offline(ipc_call_t *icall)
[1a5b252]317{
[fafb8e5]318 devman_handle_t funh = ipc_get_arg1(icall);
[a35b458]319
[0f0f8bc]320 /*
[4d94002d]321 * Look the function up. Bump reference count so that
[0f0f8bc]322 * the function continues to exist until we return
323 * from the driver.
324 */
[1a5b252]325 fibril_mutex_lock(&functions_mutex);
[a35b458]326
[77ad86c]327 ddf_fun_t *fun = driver_get_function(funh);
[0f0f8bc]328 if (fun != NULL)
329 fun_add_ref(fun);
[a35b458]330
[1a5b252]331 fibril_mutex_unlock(&functions_mutex);
[a35b458]332
[1a5b252]333 if (fun == NULL) {
[984a9ba]334 async_answer_0(icall, ENOENT);
[1a5b252]335 return;
336 }
[a35b458]337
[0f0f8bc]338 /* Call driver entry point */
[5a6cc679]339 errno_t rc;
[a35b458]340
[1a5b252]341 if (driver->driver_ops->fun_offline != NULL)
342 rc = driver->driver_ops->fun_offline(fun);
343 else
344 rc = ENOTSUP;
[a35b458]345
[984a9ba]346 async_answer_0(icall, rc);
[1a5b252]347}
348
[984a9ba]349static void driver_stop(ipc_call_t *icall)
[81685dd9]350{
351 /* Prevent new devices from being added */
352 fibril_rwlock_write_lock(&stopping_lock);
353 stopping = true;
354
355 /* Check if there are any devices */
356 fibril_mutex_lock(&devices_mutex);
357 if (list_first(&devices) != NULL) {
358 /* Devices exist, roll back */
359 fibril_mutex_unlock(&devices_mutex);
360 stopping = false;
361 fibril_rwlock_write_unlock(&stopping_lock);
[984a9ba]362 async_answer_0(icall, EBUSY);
[81685dd9]363 return;
364 }
365
366 fibril_rwlock_write_unlock(&stopping_lock);
367
368 /* There should be no functions at this point */
369 fibril_mutex_lock(&functions_mutex);
370 assert(list_first(&functions) == NULL);
371 fibril_mutex_unlock(&functions_mutex);
372
373 /* Reply with success and terminate */
[984a9ba]374 async_answer_0(icall, EOK);
[81685dd9]375 exit(0);
376}
377
[984a9ba]378static void driver_connection_devman(ipc_call_t *icall, void *arg)
[c16cf62]379{
380 /* Accept connection */
[beb83c1]381 async_accept_0(icall);
[a35b458]382
[79ae36dd]383 while (true) {
[c16cf62]384 ipc_call_t call;
[984a9ba]385 async_get_call(&call);
[a35b458]386
[fafb8e5]387 if (!ipc_get_imethod(&call)) {
[889cdb1]388 async_answer_0(&call, EOK);
[79ae36dd]389 break;
[889cdb1]390 }
[a35b458]391
[fafb8e5]392 switch (ipc_get_imethod(&call)) {
[1a5b252]393 case DRIVER_DEV_ADD:
[984a9ba]394 driver_dev_add(&call);
[c16cf62]395 break;
[1a5b252]396 case DRIVER_DEV_REMOVE:
[984a9ba]397 driver_dev_remove(&call);
[1a5b252]398 break;
[80a96d2]399 case DRIVER_DEV_GONE:
[984a9ba]400 driver_dev_gone(&call);
[80a96d2]401 break;
[8300c72]402 case DRIVER_DEV_QUIESCE:
403 driver_dev_quiesce(&call);
404 break;
[1a5b252]405 case DRIVER_FUN_ONLINE:
[984a9ba]406 driver_fun_online(&call);
[1a5b252]407 break;
408 case DRIVER_FUN_OFFLINE:
[984a9ba]409 driver_fun_offline(&call);
[1a5b252]410 break;
[81685dd9]411 case DRIVER_STOP:
[984a9ba]412 driver_stop(&call);
[81685dd9]413 break;
[c16cf62]414 default:
[984a9ba]415 async_answer_0(&call, ENOTSUP);
[c16cf62]416 }
[52b7b1bb]417 }
[c16cf62]418}
419
[79ae36dd]420/** Generic client connection handler both for applications and drivers.
421 *
422 * @param drv True for driver client, false for other clients
423 * (applications, services, etc.).
[52b7b1bb]424 *
[a1769ee]425 */
[984a9ba]426static void driver_connection_gen(ipc_call_t *icall, bool drv)
[52b7b1bb]427{
[7a252ec8]428 /*
429 * Answer the first IPC_M_CONNECT_ME_TO call and remember the handle of
430 * the device to which the client connected.
431 */
[fafb8e5]432 devman_handle_t handle = ipc_get_arg2(icall);
[1a5b252]433
434 fibril_mutex_lock(&functions_mutex);
435 ddf_fun_t *fun = driver_get_function(handle);
[b454a22]436 if (fun != NULL)
437 fun_add_ref(fun);
[1a5b252]438 fibril_mutex_unlock(&functions_mutex);
[a35b458]439
[8b1e15ac]440 if (fun == NULL) {
441 printf("%s: driver_connection_gen error - no function with handle"
[7e752b2]442 " %" PRIun " was found.\n", driver->name, handle);
[984a9ba]443 async_answer_0(icall, ENOENT);
[a1769ee]444 return;
445 }
[a35b458]446
[ff65e91]447 if (fun->conn_handler != NULL) {
448 /* Driver has a custom connection handler. */
[984a9ba]449 (*fun->conn_handler)(icall, (void *)fun);
[b454a22]450 fun_del_ref(fun);
[ff65e91]451 return;
452 }
[a35b458]453
[7a252ec8]454 /*
455 * TODO - if the client is not a driver, check whether it is allowed to
456 * use the device.
457 */
[a35b458]458
[5a6cc679]459 errno_t ret = EOK;
[8b1e15ac]460 /* Open device function */
461 if (fun->ops != NULL && fun->ops->open != NULL)
462 ret = (*fun->ops->open)(fun);
[a35b458]463
[b454a22]464 if (ret != EOK) {
[beb83c1]465 async_answer_0(icall, ret);
[b454a22]466 fun_del_ref(fun);
[a6e54c5d]467 return;
[b454a22]468 }
[a35b458]469
[beb83c1]470 async_accept_0(icall);
471
[79ae36dd]472 while (true) {
[a1769ee]473 ipc_call_t call;
[984a9ba]474 async_get_call(&call);
475
[fafb8e5]476 sysarg_t method = ipc_get_imethod(&call);
[a35b458]477
[79ae36dd]478 if (!method) {
[8b1e15ac]479 /* Close device function */
480 if (fun->ops != NULL && fun->ops->close != NULL)
481 (*fun->ops->close)(fun);
[984a9ba]482 async_answer_0(&call, EOK);
[b454a22]483 fun_del_ref(fun);
[a1769ee]484 return;
[79ae36dd]485 }
[a35b458]486
[79ae36dd]487 /* Convert ipc interface id to interface index */
[a35b458]488
[79ae36dd]489 int iface_idx = DEV_IFACE_IDX(method);
[a35b458]490
[79ae36dd]491 if (!is_valid_iface_idx(iface_idx)) {
492 remote_handler_t *default_handler =
493 function_get_default_handler(fun);
494 if (default_handler != NULL) {
[984a9ba]495 (*default_handler)(fun, &call);
[79a141a]496 continue;
[52b7b1bb]497 }
[a35b458]498
[7a252ec8]499 /*
[79ae36dd]500 * Function has no such interface and
501 * default handler is not provided.
[7a252ec8]502 */
[79ae36dd]503 printf("%s: driver_connection_gen error - "
504 "invalid interface id %d.",
505 driver->name, iface_idx);
[984a9ba]506 async_answer_0(&call, ENOTSUP);
[79a141a]507 continue;
[79ae36dd]508 }
[a35b458]509
[79ae36dd]510 /* Calling one of the function's interfaces */
[a35b458]511
[79ae36dd]512 /* Get the interface ops structure. */
513 void *ops = function_get_ops(fun, iface_idx);
514 if (ops == NULL) {
515 printf("%s: driver_connection_gen error - ", driver->name);
516 printf("Function with handle %" PRIun " has no interface "
517 "with id %d.\n", handle, iface_idx);
[984a9ba]518 async_answer_0(&call, ENOTSUP);
[79a141a]519 continue;
[79ae36dd]520 }
[a35b458]521
[79ae36dd]522 /*
523 * Get the corresponding interface for remote request
524 * handling ("remote interface").
525 */
[d0ca4c5]526 const remote_iface_t *rem_iface = get_remote_iface(iface_idx);
[79ae36dd]527 assert(rem_iface != NULL);
[a35b458]528
[79ae36dd]529 /* get the method of the remote interface */
[fafb8e5]530 sysarg_t iface_method_idx = ipc_get_arg1(&call);
[79ae36dd]531 remote_iface_func_ptr_t iface_method_ptr =
532 get_remote_method(rem_iface, iface_method_idx);
533 if (iface_method_ptr == NULL) {
534 /* The interface has not such method */
535 printf("%s: driver_connection_gen error - "
536 "invalid interface method.", driver->name);
[984a9ba]537 async_answer_0(&call, ENOTSUP);
[79a141a]538 continue;
[a1769ee]539 }
[a35b458]540
[79ae36dd]541 /*
542 * Call the remote interface's method, which will
543 * receive parameters from the remote client and it will
544 * pass it to the corresponding local interface method
545 * associated with the function by its driver.
546 */
[984a9ba]547 (*iface_method_ptr)(fun, ops, &call);
[a1769ee]548 }
549}
550
[984a9ba]551static void driver_connection_driver(ipc_call_t *icall, void *arg)
[c16cf62]552{
[984a9ba]553 driver_connection_gen(icall, true);
[c16cf62]554}
555
[984a9ba]556static void driver_connection_client(ipc_call_t *icall, void *arg)
[c16cf62]557{
[984a9ba]558 driver_connection_gen(icall, false);
[c16cf62]559}
560
[5fdd7c3]561/** Create new device structure.
562 *
563 * @return The device structure.
564 */
[83a2f43]565static ddf_dev_t *create_device(void)
[5fdd7c3]566{
[83a2f43]567 ddf_dev_t *dev;
[5fdd7c3]568
[0f0f8bc]569 dev = calloc(1, sizeof(ddf_dev_t));
[8b1e15ac]570 if (dev == NULL)
571 return NULL;
[5fdd7c3]572
[d836630]573 refcount_init(&dev->refcnt);
574
[5fdd7c3]575 return dev;
576}
577
[8b1e15ac]578/** Create new function structure.
579 *
580 * @return The device structure.
581 */
[83a2f43]582static ddf_fun_t *create_function(void)
[8b1e15ac]583{
[83a2f43]584 ddf_fun_t *fun;
[8b1e15ac]585
[83a2f43]586 fun = calloc(1, sizeof(ddf_fun_t));
[8b1e15ac]587 if (fun == NULL)
588 return NULL;
589
[d836630]590 refcount_init(&fun->refcnt);
[8b1e15ac]591 init_match_ids(&fun->match_ids);
592 link_initialize(&fun->link);
593
594 return fun;
595}
596
[5fdd7c3]597/** Delete device structure.
598 *
599 * @param dev The device structure.
600 */
[83a2f43]601static void delete_device(ddf_dev_t *dev)
[5fdd7c3]602{
[56fd7cf]603 if (dev->parent_sess)
604 async_hangup(dev->parent_sess);
[5f6e25e]605 if (dev->driver_data != NULL)
606 free(dev->driver_data);
[669e9b5]607 if (dev->name)
608 free(dev->name);
[5fdd7c3]609 free(dev);
610}
611
[0f0f8bc]612/** Delete function structure.
[8b1e15ac]613 *
614 * @param dev The device structure.
615 */
[83a2f43]616static void delete_function(ddf_fun_t *fun)
[8b1e15ac]617{
618 clean_match_ids(&fun->match_ids);
[5f6e25e]619 if (fun->driver_data != NULL)
620 free(fun->driver_data);
[8b1e15ac]621 if (fun->name != NULL)
622 free(fun->name);
623 free(fun);
624}
625
[0f0f8bc]626/** Increase device reference count. */
627static void dev_add_ref(ddf_dev_t *dev)
628{
[498ced1]629 refcount_up(&dev->refcnt);
[0f0f8bc]630}
631
632/** Decrease device reference count.
633 *
634 * Free the device structure if the reference count drops to zero.
635 */
636static void dev_del_ref(ddf_dev_t *dev)
637{
[498ced1]638 if (refcount_down(&dev->refcnt))
[0f0f8bc]639 delete_device(dev);
640}
641
[bfe7867]642/** Increase function reference count.
643 *
644 * This also increases reference count on the device. The device structure
645 * will thus not be deallocated while there are some associated function
646 * structures.
647 */
[0f0f8bc]648static void fun_add_ref(ddf_fun_t *fun)
649{
[bfe7867]650 dev_add_ref(fun->dev);
[498ced1]651 refcount_up(&fun->refcnt);
[0f0f8bc]652}
653
654/** Decrease function reference count.
655 *
656 * Free the function structure if the reference count drops to zero.
657 */
658static void fun_del_ref(ddf_fun_t *fun)
659{
[bfe7867]660 ddf_dev_t *dev = fun->dev;
661
[498ced1]662 if (refcount_down(&fun->refcnt))
[0f0f8bc]663 delete_function(fun);
[bfe7867]664
665 dev_del_ref(dev);
[0f0f8bc]666}
667
[c5be39b]668/** Allocate driver-specific device data. */
[bf31e3f]669void *ddf_dev_data_alloc(ddf_dev_t *dev, size_t size)
[c5be39b]670{
671 assert(dev->driver_data == NULL);
[a35b458]672
[77ad86c]673 void *data = calloc(1, size);
[c5be39b]674 if (data == NULL)
675 return NULL;
[a35b458]676
[c5be39b]677 dev->driver_data = data;
678 return data;
679}
680
[56fd7cf]681/** Return driver-specific device data. */
682void *ddf_dev_data_get(ddf_dev_t *dev)
683{
684 return dev->driver_data;
685}
686
687/** Get device handle. */
688devman_handle_t ddf_dev_get_handle(ddf_dev_t *dev)
689{
690 return dev->handle;
691}
692
693/** Return device name.
694 *
695 * @param dev Device
696 * @return Device name. Valid as long as @a dev is valid.
697 */
698const char *ddf_dev_get_name(ddf_dev_t *dev)
699{
700 return dev->name;
701}
702
703/** Return existing session with the parent function.
704 *
705 * @param dev Device
[2fd26bb]706 * @return Session with parent function or NULL upon failure
[56fd7cf]707 */
708async_sess_t *ddf_dev_parent_sess_get(ddf_dev_t *dev)
709{
[2fd26bb]710 if (dev->parent_sess == NULL) {
711 dev->parent_sess = devman_parent_device_connect(dev->handle,
712 IPC_FLAG_BLOCKING);
713 }
714
[56fd7cf]715 return dev->parent_sess;
716}
717
718/** Set function name (if it was not specified when node was created.)
719 *
720 * @param dev Device whose name has not been set yet
721 * @param name Name, will be copied
722 * @return EOK on success, ENOMEM if out of memory
723 */
[5a6cc679]724errno_t ddf_fun_set_name(ddf_fun_t *dev, const char *name)
[56fd7cf]725{
726 assert(dev->name == NULL);
727
728 dev->name = str_dup(name);
729 if (dev->name == NULL)
730 return ENOENT;
731
732 return EOK;
733}
734
735/** Get device to which function belongs. */
736ddf_dev_t *ddf_fun_get_dev(ddf_fun_t *fun)
737{
738 return fun->dev;
739}
740
741/** Get function handle.
742 *
743 * XXX USB uses this, but its use should be eliminated.
744 */
745devman_handle_t ddf_fun_get_handle(ddf_fun_t *fun)
746{
747 return fun->handle;
748}
749
[97a62fe]750/** Create a DDF function node.
751 *
752 * Create a DDF function (in memory). Both child devices and external clients
753 * communicate with a device via its functions.
754 *
755 * The created function node is fully formed, but only exists in the memory
756 * of the client task. In order to be visible to the system, the function
757 * must be bound using ddf_fun_bind().
758 *
759 * This function should only fail if there is not enough free memory.
760 * Specifically, this function succeeds even if @a dev already has
[56fd7cf]761 * a (bound) function with the same name. @a name can be NULL in which
762 * case the caller will set the name later using ddf_fun_set_name().
763 * He must do this before binding the function.
[97a62fe]764 *
765 * Type: A function of type fun_inner indicates that DDF should attempt
766 * to attach child devices to the function. fun_exposed means that
767 * the function should be exported to external clients (applications).
768 *
769 * @param dev Device to which we are adding function
770 * @param ftype Type of function (fun_inner or fun_exposed)
[56fd7cf]771 * @param name Name of function or NULL
[97a62fe]772 *
773 * @return New function or @c NULL if memory is not available
774 */
[83a2f43]775ddf_fun_t *ddf_fun_create(ddf_dev_t *dev, fun_type_t ftype, const char *name)
[97a62fe]776{
[77ad86c]777 ddf_fun_t *fun = create_function();
[97a62fe]778 if (fun == NULL)
779 return NULL;
[a35b458]780
[bfe7867]781 fun->dev = dev;
[9df0f64]782 dev_add_ref(fun->dev);
[a35b458]783
[97a62fe]784 fun->bound = false;
785 fun->ftype = ftype;
[a35b458]786
[56fd7cf]787 if (name != NULL) {
788 fun->name = str_dup(name);
789 if (fun->name == NULL) {
[9df0f64]790 fun_del_ref(fun); /* fun is destroyed */
[56fd7cf]791 return NULL;
792 }
[97a62fe]793 }
[a35b458]794
[97a62fe]795 return fun;
796}
797
[c5be39b]798/** Allocate driver-specific function data. */
[bf31e3f]799void *ddf_fun_data_alloc(ddf_fun_t *fun, size_t size)
[c5be39b]800{
801 assert(fun->bound == false);
802 assert(fun->driver_data == NULL);
[a35b458]803
[77ad86c]804 void *data = calloc(1, size);
[c5be39b]805 if (data == NULL)
806 return NULL;
[a35b458]807
[c5be39b]808 fun->driver_data = data;
809 return data;
810}
811
[56fd7cf]812/** Return driver-specific function data. */
813void *ddf_fun_data_get(ddf_fun_t *fun)
814{
815 return fun->driver_data;
816}
817
818/** Return function name.
819 *
820 * @param fun Function
821 * @return Function name. Valid as long as @a fun is valid.
822 */
823const char *ddf_fun_get_name(ddf_fun_t *fun)
824{
825 return fun->name;
826}
827
[97a62fe]828/** Destroy DDF function node.
829 *
830 * Destroy a function previously created with ddf_fun_create(). The function
831 * must not be bound.
832 *
[77ad86c]833 * @param fun Function to destroy
834 *
[97a62fe]835 */
[83a2f43]836void ddf_fun_destroy(ddf_fun_t *fun)
[97a62fe]837{
838 assert(fun->bound == false);
[a35b458]839
[0f0f8bc]840 /*
841 * Drop the reference added by ddf_fun_create(). This will deallocate
842 * the function as soon as all other references are dropped (i.e.
843 * as soon control leaves all driver entry points called in context
844 * of this function.
845 */
846 fun_del_ref(fun);
[97a62fe]847}
848
[af6b5157]849static void *function_get_ops(ddf_fun_t *fun, dev_inferface_idx_t idx)
[5fdd7c3]850{
851 assert(is_valid_iface_idx(idx));
[8b1e15ac]852 if (fun->ops == NULL)
[5fdd7c3]853 return NULL;
[a35b458]854
[8b1e15ac]855 return fun->ops->interfaces[idx];
[5fdd7c3]856}
857
[97a62fe]858/** Bind a function node.
859 *
860 * Bind the specified function to the system. This effectively makes
861 * the function visible to the system (uploads it to the server).
862 *
863 * This function can fail for several reasons. Specifically,
864 * it will fail if the device already has a bound function of
865 * the same name.
866 *
[77ad86c]867 * @param fun Function to bind
868 *
[cde999a]869 * @return EOK on success or an error code
[77ad86c]870 *
[97a62fe]871 */
[5a6cc679]872errno_t ddf_fun_bind(ddf_fun_t *fun)
[7707954]873{
[d0dd7b5]874 assert(fun->bound == false);
[8b1e15ac]875 assert(fun->name != NULL);
[9e79a2f]876 assert(fun->dev != NULL);
[a35b458]877
[8b1e15ac]878 add_to_functions_list(fun);
[5a6cc679]879 errno_t res = devman_add_function(fun->name, fun->ftype, &fun->match_ids,
[97a62fe]880 fun->dev->handle, &fun->handle);
[36f2b3e]881 if (res != EOK) {
[8b1e15ac]882 remove_from_functions_list(fun);
[df747b9c]883 return res;
[36f2b3e]884 }
[a35b458]885
[97a62fe]886 fun->bound = true;
[df747b9c]887 return res;
[7707954]888}
889
[d0dd7b5]890/** Unbind a function node.
891 *
892 * Unbind the specified function from the system. This effectively makes
893 * the function invisible to the system.
894 *
[77ad86c]895 * @param fun Function to unbind
896 *
[cde999a]897 * @return EOK on success or an error code
[77ad86c]898 *
[d0dd7b5]899 */
[5a6cc679]900errno_t ddf_fun_unbind(ddf_fun_t *fun)
[d0dd7b5]901{
902 assert(fun->bound == true);
[a35b458]903
[5a6cc679]904 errno_t res = devman_remove_function(fun->handle);
[d0dd7b5]905 if (res != EOK)
906 return res;
[a35b458]907
[d0dd7b5]908 remove_from_functions_list(fun);
[a35b458]909
[d0dd7b5]910 fun->bound = false;
911 return EOK;
912}
913
[1a5b252]914/** Online function.
915 *
[77ad86c]916 * @param fun Function to online
917 *
[cde999a]918 * @return EOK on success or an error code
[77ad86c]919 *
[1a5b252]920 */
[5a6cc679]921errno_t ddf_fun_online(ddf_fun_t *fun)
[1a5b252]922{
923 assert(fun->bound == true);
[a35b458]924
[5a6cc679]925 errno_t res = devman_drv_fun_online(fun->handle);
[1a5b252]926 if (res != EOK)
927 return res;
[a35b458]928
[1a5b252]929 return EOK;
930}
931
932/** Offline function.
933 *
[77ad86c]934 * @param fun Function to offline
935 *
[cde999a]936 * @return EOK on success or an error code
[77ad86c]937 *
[1a5b252]938 */
[5a6cc679]939errno_t ddf_fun_offline(ddf_fun_t *fun)
[1a5b252]940{
941 assert(fun->bound == true);
[a35b458]942
[5a6cc679]943 errno_t res = devman_drv_fun_offline(fun->handle);
[8300c72]944 if (res != EOK)
945 return res;
946
947 return EOK;
948}
949
950/** Quiesce function.
951 *
952 * @param fun Function to quiesce
953 *
954 * @return EOK on success or an error code
955 *
956 */
957errno_t ddf_fun_quiesce(ddf_fun_t *fun)
958{
959 assert(fun->bound == true);
960
961 errno_t res = devman_drv_fun_quiesce(fun->handle);
[1a5b252]962 if (res != EOK)
963 return res;
[a35b458]964
[1a5b252]965 return EOK;
966}
967
[34588a80]968/** Add single match ID to inner function.
[cd0684d]969 *
970 * Construct and add a single match ID to the specified function.
[34588a80]971 * Cannot be called when the function node is bound.
[cd0684d]972 *
[77ad86c]973 * @param fun Function
974 * @param match_id_str Match string
975 * @param match_score Match score
976 *
977 * @return EOK on success.
978 * @return ENOMEM if out of memory.
979 *
[cd0684d]980 */
[5a6cc679]981errno_t ddf_fun_add_match_id(ddf_fun_t *fun, const char *match_id_str,
[cd0684d]982 int match_score)
983{
[34588a80]984 assert(fun->bound == false);
985 assert(fun->ftype == fun_inner);
[a35b458]986
[77ad86c]987 match_id_t *match_id = create_match_id();
[cd0684d]988 if (match_id == NULL)
989 return ENOMEM;
[a35b458]990
[ef9460b]991 match_id->id = str_dup(match_id_str);
[8a363ab3]992 match_id->score = match_score;
[a35b458]993
[cd0684d]994 add_match_id(&fun->match_ids, match_id);
995 return EOK;
996}
997
[56fd7cf]998/** Set function ops. */
[facc34d]999void ddf_fun_set_ops(ddf_fun_t *fun, const ddf_dev_ops_t *dev_ops)
[56fd7cf]1000{
1001 assert(fun->conn_handler == NULL);
1002 fun->ops = dev_ops;
1003}
1004
1005/** Set user-defined connection handler.
1006 *
1007 * This allows handling connections the non-devman way.
1008 */
[b688fd8]1009void ddf_fun_set_conn_handler(ddf_fun_t *fun, async_port_handler_t conn)
[56fd7cf]1010{
1011 assert(fun->ops == NULL);
1012 fun->conn_handler = conn;
1013}
1014
[5fdd7c3]1015/** Get default handler for client requests */
[af6b5157]1016static remote_handler_t *function_get_default_handler(ddf_fun_t *fun)
[5fdd7c3]1017{
[8b1e15ac]1018 if (fun->ops == NULL)
[5fdd7c3]1019 return NULL;
[8b1e15ac]1020 return fun->ops->default_handler;
[5fdd7c3]1021}
1022
[1dc4a5e]1023/** Add exposed function to category.
[34588a80]1024 *
1025 * Must only be called when the function is bound.
[77ad86c]1026 *
[34588a80]1027 */
[5a6cc679]1028errno_t ddf_fun_add_to_category(ddf_fun_t *fun, const char *cat_name)
[5fdd7c3]1029{
[34588a80]1030 assert(fun->bound == true);
1031 assert(fun->ftype == fun_exposed);
[a35b458]1032
[1dc4a5e]1033 return devman_add_device_to_category(fun->handle, cat_name);
[5fdd7c3]1034}
1035
[832cbe7]1036/** Wait for function to enter stable state.
1037 *
1038 * @param fun Function
1039 * @return EOK on success or an error code
1040 */
1041errno_t ddf_fun_wait_stable(ddf_fun_t *fun)
1042{
1043 return devman_drv_fun_wait_stable(fun->handle);
1044}
1045
[45457265]1046errno_t ddf_driver_main(const driver_t *drv)
[c16cf62]1047{
[7a252ec8]1048 /*
1049 * Remember the driver structure - driver_ops will be called by generic
1050 * handler for incoming connections.
1051 */
[c16cf62]1052 driver = drv;
[a35b458]1053
[7a252ec8]1054 /*
[033cbf82]1055 * Register driver with device manager using generic handler for
1056 * incoming connections.
[7a252ec8]1057 */
[f9b2cb4c]1058 port_id_t port;
[5a6cc679]1059 errno_t rc = async_create_port(INTERFACE_DDF_DRIVER, driver_connection_driver,
[f9b2cb4c]1060 NULL, &port);
[7b616e2]1061 if (rc != EOK) {
1062 printf("Error: Failed to create driver port.\n");
[f9b2cb4c]1063 return rc;
[7b616e2]1064 }
[a35b458]1065
[f9b2cb4c]1066 rc = async_create_port(INTERFACE_DDF_DEVMAN, driver_connection_devman,
1067 NULL, &port);
[7b616e2]1068 if (rc != EOK) {
1069 printf("Error: Failed to create devman port.\n");
[f9b2cb4c]1070 return rc;
[7b616e2]1071 }
[a35b458]1072
[f9b2cb4c]1073 async_set_fallback_port_handler(driver_connection_client, NULL);
[a35b458]1074
[f9b2cb4c]1075 rc = devman_driver_register(driver->name);
[033cbf82]1076 if (rc != EOK) {
[3ad7b1c]1077 printf("Error: Failed to register driver with device manager "
[8a637a4]1078 "(%s).\n", (rc == EEXIST) ? "driver already started" :
[3ad7b1c]1079 str_error(rc));
[a35b458]1080
[77ad86c]1081 return rc;
[033cbf82]1082 }
[a35b458]1083
[26fa82bc]1084 /* Return success from the task since server has started. */
1085 rc = task_retval(0);
[7b616e2]1086 if (rc != EOK) {
1087 printf("Error: Failed returning task value.\n");
[77ad86c]1088 return rc;
[7b616e2]1089 }
[a35b458]1090
[c16cf62]1091 async_manager();
[a35b458]1092
[7a252ec8]1093 /* Never reached. */
[77ad86c]1094 return EOK;
[c16cf62]1095}
1096
1097/**
1098 * @}
[52b7b1bb]1099 */
Note: See TracBrowser for help on using the repository browser.