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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 25a179e was 25a179e, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

IPC return values are always errno constants. Adjust types to reflect that.

In principle, IPC server is not allowed to return non-errno values via
the "main" return value, because kernel interprets it (e.g. EHANGUP).
It's still possible to return arbitrary additional return values alongside EOK,
which are not interpreted in normal communication.

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