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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3943da1 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

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