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

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

gradually introduce async ports, initial phase

The initial phase is to reimplement the traditional async client connections as an untyped fallback port. This creates the possibility to introduce ports typed by interface type gradually in later changesets.

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