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

Last change on this file since eec201d was f5837524, checked in by Jakub Jermar <jakub@…>, 7 years ago

Use user-defined labels instead of phone hashes

This commit changes the way how the async framework maps incomming calls
to connections. Instead of abusing the kernel addresses of attached
phones as identifiers, the IPC_M_CONNECT_TO_ME and IPC_M_CONNECT_ME_TO
messages allow the server to specify an arbitrary label which is
remembered in the connected phone and consequently imprinted on each
call which is routed through this phone.

The async framework uses the address of the connection structure as the
label. This removes the need for a connection hash table because each
incoming call already remembers the connection in its label.

To disambiguate this new label and the other user-defined label used for
answers, the call structure now has the request_label member for the
former and answer_label member for the latter.

This commit also moves the kernel definition of ipc_data_t to abi/ and
removes the uspace redefinition thereof. Finally, when forwarding the
IPC_M_CONNECT_TO_ME call, the phone capability and the kernel object
allocated in request_process are now correctly disposed of.

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