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

Last change on this file was ca48672, checked in by Jiri Svoboda <jiri@…>, 8 days ago

loc_service_register() needs to take a port ID argument.

  • Property mode set to 100644
File size: 24.6 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
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/**
31 * @addtogroup libdrv
32 * @{
33 */
34
35/** @file
36 */
37
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>
44#include <stdbool.h>
45#include <fibril_synch.h>
46#include <stdlib.h>
47#include <str.h>
48#include <str_error.h>
49#include <ctype.h>
50#include <inttypes.h>
51#include <devman.h>
52
53#include "dev_iface.h"
54#include "ddf/driver.h"
55#include "ddf/interrupt.h"
56#include "private/driver.h"
57
58/** Driver structure */
59static const driver_t *driver;
60
61/** Devices */
62LIST_INITIALIZE(devices);
63FIBRIL_MUTEX_INITIALIZE(devices_mutex);
64
65/** Functions */
66LIST_INITIALIZE(functions);
67FIBRIL_MUTEX_INITIALIZE(functions_mutex);
68
69FIBRIL_RWLOCK_INITIALIZE(stopping_lock);
70static bool stopping = false;
71
72static ddf_dev_t *create_device(void);
73static void delete_device(ddf_dev_t *);
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 *);
78static remote_handler_t *function_get_default_handler(ddf_fun_t *);
79static void *function_get_ops(ddf_fun_t *, dev_inferface_idx_t);
80
81static void add_to_functions_list(ddf_fun_t *fun)
82{
83 fibril_mutex_lock(&functions_mutex);
84 list_append(&fun->link, &functions);
85 fibril_mutex_unlock(&functions_mutex);
86}
87
88static void remove_from_functions_list(ddf_fun_t *fun)
89{
90 fibril_mutex_lock(&functions_mutex);
91 list_remove(&fun->link);
92 fibril_mutex_unlock(&functions_mutex);
93}
94
95static ddf_dev_t *driver_get_device(devman_handle_t handle)
96{
97 assert(fibril_mutex_is_locked(&devices_mutex));
98
99 list_foreach(devices, link, ddf_dev_t, dev) {
100 if (dev->handle == handle)
101 return dev;
102 }
103
104 return NULL;
105}
106
107static ddf_fun_t *driver_get_function(devman_handle_t handle)
108{
109 assert(fibril_mutex_is_locked(&functions_mutex));
110
111 list_foreach(functions, link, ddf_fun_t, fun) {
112 if (fun->handle == handle)
113 return fun;
114 }
115
116 return NULL;
117}
118
119static void driver_dev_add(ipc_call_t *icall)
120{
121 devman_handle_t dev_handle = ipc_get_arg1(icall);
122 devman_handle_t parent_fun_handle = ipc_get_arg2(icall);
123
124 char *dev_name = NULL;
125 errno_t rc = async_data_write_accept((void **) &dev_name, true, 0, 0, 0, 0);
126 if (rc != EOK) {
127 async_answer_0(icall, rc);
128 return;
129 }
130
131 fibril_rwlock_read_lock(&stopping_lock);
132
133 if (stopping) {
134 fibril_rwlock_read_unlock(&stopping_lock);
135 async_answer_0(icall, EIO);
136 return;
137 }
138
139 ddf_dev_t *dev = create_device();
140 if (!dev) {
141 fibril_rwlock_read_unlock(&stopping_lock);
142 free(dev_name);
143 async_answer_0(icall, ENOMEM);
144 return;
145 }
146
147 dev->handle = dev_handle;
148 dev->name = dev_name;
149
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;
155
156 errno_t res = driver->driver_ops->dev_add(dev);
157
158 if (res != EOK) {
159 fibril_rwlock_read_unlock(&stopping_lock);
160 dev_del_ref(dev);
161 async_answer_0(icall, res);
162 return;
163 }
164
165 fibril_mutex_lock(&devices_mutex);
166 list_append(&dev->link, &devices);
167 fibril_mutex_unlock(&devices_mutex);
168 fibril_rwlock_read_unlock(&stopping_lock);
169
170 async_answer_0(icall, res);
171}
172
173static void driver_dev_remove(ipc_call_t *icall)
174{
175 devman_handle_t devh = ipc_get_arg1(icall);
176
177 fibril_mutex_lock(&devices_mutex);
178 ddf_dev_t *dev = driver_get_device(devh);
179 if (dev != NULL)
180 dev_add_ref(dev);
181 fibril_mutex_unlock(&devices_mutex);
182
183 if (dev == NULL) {
184 async_answer_0(icall, ENOENT);
185 return;
186 }
187
188 errno_t rc;
189
190 if (driver->driver_ops->dev_remove != NULL)
191 rc = driver->driver_ops->dev_remove(dev);
192 else
193 rc = ENOTSUP;
194
195 if (rc == EOK) {
196 fibril_mutex_lock(&devices_mutex);
197 list_remove(&dev->link);
198 fibril_mutex_unlock(&devices_mutex);
199 dev_del_ref(dev);
200 }
201
202 dev_del_ref(dev);
203 async_answer_0(icall, rc);
204}
205
206static void driver_dev_gone(ipc_call_t *icall)
207{
208 devman_handle_t devh = ipc_get_arg1(icall);
209
210 fibril_mutex_lock(&devices_mutex);
211 ddf_dev_t *dev = driver_get_device(devh);
212 if (dev != NULL)
213 dev_add_ref(dev);
214 fibril_mutex_unlock(&devices_mutex);
215
216 if (dev == NULL) {
217 async_answer_0(icall, ENOENT);
218 return;
219 }
220
221 errno_t rc;
222
223 if (driver->driver_ops->dev_gone != NULL)
224 rc = driver->driver_ops->dev_gone(dev);
225 else
226 rc = ENOTSUP;
227
228 if (rc == EOK) {
229 fibril_mutex_lock(&devices_mutex);
230 list_remove(&dev->link);
231 fibril_mutex_unlock(&devices_mutex);
232 dev_del_ref(dev);
233 }
234
235 dev_del_ref(dev);
236 async_answer_0(icall, rc);
237}
238
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
281static void driver_fun_online(ipc_call_t *icall)
282{
283 devman_handle_t funh = ipc_get_arg1(icall);
284
285 /*
286 * Look the function up. Bump reference count so that
287 * the function continues to exist until we return
288 * from the driver.
289 */
290 fibril_mutex_lock(&functions_mutex);
291
292 ddf_fun_t *fun = driver_get_function(funh);
293 if (fun != NULL)
294 fun_add_ref(fun);
295
296 fibril_mutex_unlock(&functions_mutex);
297
298 if (fun == NULL) {
299 async_answer_0(icall, ENOENT);
300 return;
301 }
302
303 /* Call driver entry point */
304 errno_t rc;
305
306 if (driver->driver_ops->fun_online != NULL)
307 rc = driver->driver_ops->fun_online(fun);
308 else
309 rc = ENOTSUP;
310
311 fun_del_ref(fun);
312
313 async_answer_0(icall, rc);
314}
315
316static void driver_fun_offline(ipc_call_t *icall)
317{
318 devman_handle_t funh = ipc_get_arg1(icall);
319
320 /*
321 * Look the function up. Bump reference count so that
322 * the function continues to exist until we return
323 * from the driver.
324 */
325 fibril_mutex_lock(&functions_mutex);
326
327 ddf_fun_t *fun = driver_get_function(funh);
328 if (fun != NULL)
329 fun_add_ref(fun);
330
331 fibril_mutex_unlock(&functions_mutex);
332
333 if (fun == NULL) {
334 async_answer_0(icall, ENOENT);
335 return;
336 }
337
338 /* Call driver entry point */
339 errno_t rc;
340
341 if (driver->driver_ops->fun_offline != NULL)
342 rc = driver->driver_ops->fun_offline(fun);
343 else
344 rc = ENOTSUP;
345
346 async_answer_0(icall, rc);
347}
348
349static void driver_stop(ipc_call_t *icall)
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);
362 async_answer_0(icall, EBUSY);
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 */
374 async_answer_0(icall, EOK);
375 exit(0);
376}
377
378static void driver_connection_devman(ipc_call_t *icall, void *arg)
379{
380 /* Accept connection */
381 async_accept_0(icall);
382
383 while (true) {
384 ipc_call_t call;
385 async_get_call(&call);
386
387 if (!ipc_get_imethod(&call)) {
388 async_answer_0(&call, EOK);
389 break;
390 }
391
392 switch (ipc_get_imethod(&call)) {
393 case DRIVER_DEV_ADD:
394 driver_dev_add(&call);
395 break;
396 case DRIVER_DEV_REMOVE:
397 driver_dev_remove(&call);
398 break;
399 case DRIVER_DEV_GONE:
400 driver_dev_gone(&call);
401 break;
402 case DRIVER_DEV_QUIESCE:
403 driver_dev_quiesce(&call);
404 break;
405 case DRIVER_FUN_ONLINE:
406 driver_fun_online(&call);
407 break;
408 case DRIVER_FUN_OFFLINE:
409 driver_fun_offline(&call);
410 break;
411 case DRIVER_STOP:
412 driver_stop(&call);
413 break;
414 default:
415 async_answer_0(&call, ENOTSUP);
416 }
417 }
418}
419
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.).
424 *
425 */
426static void driver_connection_gen(ipc_call_t *icall, bool drv)
427{
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 */
432 devman_handle_t handle = ipc_get_arg2(icall);
433
434 fibril_mutex_lock(&functions_mutex);
435 ddf_fun_t *fun = driver_get_function(handle);
436 if (fun != NULL)
437 fun_add_ref(fun);
438 fibril_mutex_unlock(&functions_mutex);
439
440 if (fun == NULL) {
441 printf("%s: driver_connection_gen error - no function with handle"
442 " %" PRIun " was found.\n", driver->name, handle);
443 async_answer_0(icall, ENOENT);
444 return;
445 }
446
447 if (fun->conn_handler != NULL) {
448 /* Driver has a custom connection handler. */
449 (*fun->conn_handler)(icall, (void *)fun);
450 fun_del_ref(fun);
451 return;
452 }
453
454 /*
455 * TODO - if the client is not a driver, check whether it is allowed to
456 * use the device.
457 */
458
459 errno_t ret = EOK;
460 /* Open device function */
461 if (fun->ops != NULL && fun->ops->open != NULL)
462 ret = (*fun->ops->open)(fun);
463
464 if (ret != EOK) {
465 async_answer_0(icall, ret);
466 fun_del_ref(fun);
467 return;
468 }
469
470 async_accept_0(icall);
471
472 while (true) {
473 ipc_call_t call;
474 async_get_call(&call);
475
476 sysarg_t method = ipc_get_imethod(&call);
477
478 if (!method) {
479 /* Close device function */
480 if (fun->ops != NULL && fun->ops->close != NULL)
481 (*fun->ops->close)(fun);
482 async_answer_0(&call, EOK);
483 fun_del_ref(fun);
484 return;
485 }
486
487 /* Convert ipc interface id to interface index */
488
489 int iface_idx = DEV_IFACE_IDX(method);
490
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) {
495 (*default_handler)(fun, &call);
496 continue;
497 }
498
499 /*
500 * Function has no such interface and
501 * default handler is not provided.
502 */
503 printf("%s: driver_connection_gen error - "
504 "invalid interface id %d.",
505 driver->name, iface_idx);
506 async_answer_0(&call, ENOTSUP);
507 continue;
508 }
509
510 /* Calling one of the function's interfaces */
511
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);
518 async_answer_0(&call, ENOTSUP);
519 continue;
520 }
521
522 /*
523 * Get the corresponding interface for remote request
524 * handling ("remote interface").
525 */
526 const remote_iface_t *rem_iface = get_remote_iface(iface_idx);
527 assert(rem_iface != NULL);
528
529 /* get the method of the remote interface */
530 sysarg_t iface_method_idx = ipc_get_arg1(&call);
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);
537 async_answer_0(&call, ENOTSUP);
538 continue;
539 }
540
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 */
547 (*iface_method_ptr)(fun, ops, &call);
548 }
549}
550
551static void driver_connection_driver(ipc_call_t *icall, void *arg)
552{
553 driver_connection_gen(icall, true);
554}
555
556static void driver_connection_client(ipc_call_t *icall, void *arg)
557{
558 driver_connection_gen(icall, false);
559}
560
561/** Create new device structure.
562 *
563 * @return The device structure.
564 */
565static ddf_dev_t *create_device(void)
566{
567 ddf_dev_t *dev;
568
569 dev = calloc(1, sizeof(ddf_dev_t));
570 if (dev == NULL)
571 return NULL;
572
573 refcount_init(&dev->refcnt);
574
575 return dev;
576}
577
578/** Create new function structure.
579 *
580 * @return The device structure.
581 */
582static ddf_fun_t *create_function(void)
583{
584 ddf_fun_t *fun;
585
586 fun = calloc(1, sizeof(ddf_fun_t));
587 if (fun == NULL)
588 return NULL;
589
590 refcount_init(&fun->refcnt);
591 init_match_ids(&fun->match_ids);
592 link_initialize(&fun->link);
593
594 return fun;
595}
596
597/** Delete device structure.
598 *
599 * @param dev The device structure.
600 */
601static void delete_device(ddf_dev_t *dev)
602{
603 if (dev->parent_sess)
604 async_hangup(dev->parent_sess);
605 if (dev->driver_data != NULL)
606 free(dev->driver_data);
607 if (dev->name)
608 free(dev->name);
609 free(dev);
610}
611
612/** Delete function structure.
613 *
614 * @param dev The device structure.
615 */
616static void delete_function(ddf_fun_t *fun)
617{
618 clean_match_ids(&fun->match_ids);
619 if (fun->driver_data != NULL)
620 free(fun->driver_data);
621 if (fun->name != NULL)
622 free(fun->name);
623 free(fun);
624}
625
626/** Increase device reference count. */
627static void dev_add_ref(ddf_dev_t *dev)
628{
629 refcount_up(&dev->refcnt);
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{
638 if (refcount_down(&dev->refcnt))
639 delete_device(dev);
640}
641
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 */
648static void fun_add_ref(ddf_fun_t *fun)
649{
650 dev_add_ref(fun->dev);
651 refcount_up(&fun->refcnt);
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{
660 ddf_dev_t *dev = fun->dev;
661
662 if (refcount_down(&fun->refcnt))
663 delete_function(fun);
664
665 dev_del_ref(dev);
666}
667
668/** Allocate driver-specific device data. */
669void *ddf_dev_data_alloc(ddf_dev_t *dev, size_t size)
670{
671 assert(dev->driver_data == NULL);
672
673 void *data = calloc(1, size);
674 if (data == NULL)
675 return NULL;
676
677 dev->driver_data = data;
678 return data;
679}
680
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
706 * @return Session with parent function or NULL upon failure
707 */
708async_sess_t *ddf_dev_parent_sess_get(ddf_dev_t *dev)
709{
710 if (dev->parent_sess == NULL) {
711 dev->parent_sess = devman_parent_device_connect(dev->handle,
712 IPC_FLAG_BLOCKING);
713 }
714
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 */
724errno_t ddf_fun_set_name(ddf_fun_t *dev, const char *name)
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
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
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.
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)
771 * @param name Name of function or NULL
772 *
773 * @return New function or @c NULL if memory is not available
774 */
775ddf_fun_t *ddf_fun_create(ddf_dev_t *dev, fun_type_t ftype, const char *name)
776{
777 ddf_fun_t *fun = create_function();
778 if (fun == NULL)
779 return NULL;
780
781 fun->dev = dev;
782 dev_add_ref(fun->dev);
783
784 fun->bound = false;
785 fun->ftype = ftype;
786
787 if (name != NULL) {
788 fun->name = str_dup(name);
789 if (fun->name == NULL) {
790 fun_del_ref(fun); /* fun is destroyed */
791 return NULL;
792 }
793 }
794
795 return fun;
796}
797
798/** Allocate driver-specific function data. */
799void *ddf_fun_data_alloc(ddf_fun_t *fun, size_t size)
800{
801 assert(fun->bound == false);
802 assert(fun->driver_data == NULL);
803
804 void *data = calloc(1, size);
805 if (data == NULL)
806 return NULL;
807
808 fun->driver_data = data;
809 return data;
810}
811
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
828/** Destroy DDF function node.
829 *
830 * Destroy a function previously created with ddf_fun_create(). The function
831 * must not be bound.
832 *
833 * @param fun Function to destroy
834 *
835 */
836void ddf_fun_destroy(ddf_fun_t *fun)
837{
838 assert(fun->bound == false);
839
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);
847}
848
849static void *function_get_ops(ddf_fun_t *fun, dev_inferface_idx_t idx)
850{
851 assert(is_valid_iface_idx(idx));
852 if (fun->ops == NULL)
853 return NULL;
854
855 return fun->ops->interfaces[idx];
856}
857
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 *
867 * @param fun Function to bind
868 *
869 * @return EOK on success or an error code
870 *
871 */
872errno_t ddf_fun_bind(ddf_fun_t *fun)
873{
874 assert(fun->bound == false);
875 assert(fun->name != NULL);
876 assert(fun->dev != NULL);
877
878 add_to_functions_list(fun);
879 errno_t res = devman_add_function(fun->name, fun->ftype, &fun->match_ids,
880 fun->dev->handle, &fun->handle);
881 if (res != EOK) {
882 remove_from_functions_list(fun);
883 return res;
884 }
885
886 fun->bound = true;
887 return res;
888}
889
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 *
895 * @param fun Function to unbind
896 *
897 * @return EOK on success or an error code
898 *
899 */
900errno_t ddf_fun_unbind(ddf_fun_t *fun)
901{
902 assert(fun->bound == true);
903
904 errno_t res = devman_remove_function(fun->handle);
905 if (res != EOK)
906 return res;
907
908 remove_from_functions_list(fun);
909
910 fun->bound = false;
911 return EOK;
912}
913
914/** Online function.
915 *
916 * @param fun Function to online
917 *
918 * @return EOK on success or an error code
919 *
920 */
921errno_t ddf_fun_online(ddf_fun_t *fun)
922{
923 assert(fun->bound == true);
924
925 errno_t res = devman_drv_fun_online(fun->handle);
926 if (res != EOK)
927 return res;
928
929 return EOK;
930}
931
932/** Offline function.
933 *
934 * @param fun Function to offline
935 *
936 * @return EOK on success or an error code
937 *
938 */
939errno_t ddf_fun_offline(ddf_fun_t *fun)
940{
941 assert(fun->bound == true);
942
943 errno_t res = devman_drv_fun_offline(fun->handle);
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);
962 if (res != EOK)
963 return res;
964
965 return EOK;
966}
967
968/** Add single match ID to inner function.
969 *
970 * Construct and add a single match ID to the specified function.
971 * Cannot be called when the function node is bound.
972 *
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 *
980 */
981errno_t ddf_fun_add_match_id(ddf_fun_t *fun, const char *match_id_str,
982 int match_score)
983{
984 assert(fun->bound == false);
985 assert(fun->ftype == fun_inner);
986
987 match_id_t *match_id = create_match_id();
988 if (match_id == NULL)
989 return ENOMEM;
990
991 match_id->id = str_dup(match_id_str);
992 match_id->score = match_score;
993
994 add_match_id(&fun->match_ids, match_id);
995 return EOK;
996}
997
998/** Set function ops. */
999void ddf_fun_set_ops(ddf_fun_t *fun, const ddf_dev_ops_t *dev_ops)
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 */
1009void ddf_fun_set_conn_handler(ddf_fun_t *fun, async_port_handler_t conn)
1010{
1011 assert(fun->ops == NULL);
1012 fun->conn_handler = conn;
1013}
1014
1015/** Get default handler for client requests */
1016static remote_handler_t *function_get_default_handler(ddf_fun_t *fun)
1017{
1018 if (fun->ops == NULL)
1019 return NULL;
1020 return fun->ops->default_handler;
1021}
1022
1023/** Add exposed function to category.
1024 *
1025 * Must only be called when the function is bound.
1026 *
1027 */
1028errno_t ddf_fun_add_to_category(ddf_fun_t *fun, const char *cat_name)
1029{
1030 assert(fun->bound == true);
1031 assert(fun->ftype == fun_exposed);
1032
1033 return devman_add_device_to_category(fun->handle, cat_name);
1034}
1035
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
1046errno_t ddf_driver_main(const driver_t *drv)
1047{
1048 port_id_t drv_port;
1049 port_id_t devman_port;
1050
1051 /*
1052 * Remember the driver structure - driver_ops will be called by generic
1053 * handler for incoming connections.
1054 */
1055 driver = drv;
1056
1057 /*
1058 * Register driver with device manager using generic handler for
1059 * incoming connections.
1060 */
1061 errno_t rc = async_create_port(INTERFACE_DDF_DRIVER,
1062 driver_connection_driver, NULL, &drv_port);
1063 if (rc != EOK) {
1064 printf("Error: Failed to create driver port.\n");
1065 return rc;
1066 }
1067
1068 rc = async_create_port(INTERFACE_DDF_DEVMAN, driver_connection_devman,
1069 NULL, &devman_port);
1070 if (rc != EOK) {
1071 printf("Error: Failed to create devman port.\n");
1072 async_port_destroy(drv_port);
1073 return rc;
1074 }
1075
1076 async_set_fallback_port_handler(driver_connection_client, NULL);
1077
1078 rc = devman_driver_register(driver->name);
1079 if (rc != EOK) {
1080 printf("Error: Failed to register driver with device manager "
1081 "(%s).\n", (rc == EEXIST) ? "driver already started" :
1082 str_error(rc));
1083 async_port_destroy(devman_port);
1084 async_port_destroy(drv_port);
1085 return rc;
1086 }
1087
1088 /* Return success from the task since server has started. */
1089 rc = task_retval(0);
1090 if (rc != EOK) {
1091 printf("Error: Failed returning task value.\n");
1092 // XXX devman_driver_unregister
1093 async_port_destroy(devman_port);
1094 async_port_destroy(drv_port);
1095 return rc;
1096 }
1097
1098 async_manager();
1099
1100 /* Never reached. */
1101 return EOK;
1102}
1103
1104/**
1105 * @}
1106 */
Note: See TracBrowser for help on using the repository browser.