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

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

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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