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

Last change on this file since accdf882 was 832cbe7, checked in by Jiri Svoboda <jiri@…>, 5 months ago

Add proper IDE PCI to ISA fallback mechanism.

To determine if legacy IDE I/O ports are free, isa-ide asks isa,
who asks pciintel. pciintel waits for bus enumeration to complete,
then waits for all functions except the one who is asking
(which is ISA bus) to stabilize. During attach pci-ide will claim
the legacy IDE ports. Thus, if at this point legacy IDE ports
are unclaimed, pciintel tells ISA they are free, which tells isa-ide,
which continues to attach. If they are not free, isa-ide will not
attach.

This works for all use cases, including system without PCI bus,
system with PCI bus, but no (or disabled) PCI IDE, system with PCI
IDE with unrecognized VID/PID (which we will handle in legacy ISA mode).

  • Property mode set to 100644
File size: 23.1 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * Copyright (c) 2010 Lenka Trochtova
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 dev->handle = dev_handle;
148 dev->name = dev_name;
149
150 /*
151 * Currently not used, parent fun handle is stored in context
152 * of the connection to the parent device driver.
153 */
154 (void) parent_fun_handle;
155
156 errno_t res = driver->driver_ops->dev_add(dev);
157
158 if (res != EOK) {
159 fibril_rwlock_read_unlock(&stopping_lock);
160 dev_del_ref(dev);
161 async_answer_0(icall, res);
162 return;
163 }
164
165 fibril_mutex_lock(&devices_mutex);
166 list_append(&dev->link, &devices);
167 fibril_mutex_unlock(&devices_mutex);
168 fibril_rwlock_read_unlock(&stopping_lock);
169
170 async_answer_0(icall, res);
171}
172
173static void driver_dev_remove(ipc_call_t *icall)
174{
175 devman_handle_t devh = ipc_get_arg1(icall);
176
177 fibril_mutex_lock(&devices_mutex);
178 ddf_dev_t *dev = driver_get_device(devh);
179 if (dev != NULL)
180 dev_add_ref(dev);
181 fibril_mutex_unlock(&devices_mutex);
182
183 if (dev == NULL) {
184 async_answer_0(icall, ENOENT);
185 return;
186 }
187
188 errno_t rc;
189
190 if (driver->driver_ops->dev_remove != NULL)
191 rc = driver->driver_ops->dev_remove(dev);
192 else
193 rc = ENOTSUP;
194
195 if (rc == EOK) {
196 fibril_mutex_lock(&devices_mutex);
197 list_remove(&dev->link);
198 fibril_mutex_unlock(&devices_mutex);
199 dev_del_ref(dev);
200 }
201
202 dev_del_ref(dev);
203 async_answer_0(icall, rc);
204}
205
206static void driver_dev_gone(ipc_call_t *icall)
207{
208 devman_handle_t devh = ipc_get_arg1(icall);
209
210 fibril_mutex_lock(&devices_mutex);
211 ddf_dev_t *dev = driver_get_device(devh);
212 if (dev != NULL)
213 dev_add_ref(dev);
214 fibril_mutex_unlock(&devices_mutex);
215
216 if (dev == NULL) {
217 async_answer_0(icall, ENOENT);
218 return;
219 }
220
221 errno_t rc;
222
223 if (driver->driver_ops->dev_gone != NULL)
224 rc = driver->driver_ops->dev_gone(dev);
225 else
226 rc = ENOTSUP;
227
228 if (rc == EOK) {
229 fibril_mutex_lock(&devices_mutex);
230 list_remove(&dev->link);
231 fibril_mutex_unlock(&devices_mutex);
232 dev_del_ref(dev);
233 }
234
235 dev_del_ref(dev);
236 async_answer_0(icall, rc);
237}
238
239static void driver_fun_online(ipc_call_t *icall)
240{
241 devman_handle_t funh = ipc_get_arg1(icall);
242
243 /*
244 * Look the function up. Bump reference count so that
245 * the function continues to exist until we return
246 * from the driver.
247 */
248 fibril_mutex_lock(&functions_mutex);
249
250 ddf_fun_t *fun = driver_get_function(funh);
251 if (fun != NULL)
252 fun_add_ref(fun);
253
254 fibril_mutex_unlock(&functions_mutex);
255
256 if (fun == NULL) {
257 async_answer_0(icall, ENOENT);
258 return;
259 }
260
261 /* Call driver entry point */
262 errno_t rc;
263
264 if (driver->driver_ops->fun_online != NULL)
265 rc = driver->driver_ops->fun_online(fun);
266 else
267 rc = ENOTSUP;
268
269 fun_del_ref(fun);
270
271 async_answer_0(icall, rc);
272}
273
274static void driver_fun_offline(ipc_call_t *icall)
275{
276 devman_handle_t funh = ipc_get_arg1(icall);
277
278 /*
279 * Look the function up. Bump reference count so that
280 * the function continues to exist until we return
281 * from the driver.
282 */
283 fibril_mutex_lock(&functions_mutex);
284
285 ddf_fun_t *fun = driver_get_function(funh);
286 if (fun != NULL)
287 fun_add_ref(fun);
288
289 fibril_mutex_unlock(&functions_mutex);
290
291 if (fun == NULL) {
292 async_answer_0(icall, ENOENT);
293 return;
294 }
295
296 /* Call driver entry point */
297 errno_t rc;
298
299 if (driver->driver_ops->fun_offline != NULL)
300 rc = driver->driver_ops->fun_offline(fun);
301 else
302 rc = ENOTSUP;
303
304 async_answer_0(icall, rc);
305}
306
307static void driver_stop(ipc_call_t *icall)
308{
309 /* Prevent new devices from being added */
310 fibril_rwlock_write_lock(&stopping_lock);
311 stopping = true;
312
313 /* Check if there are any devices */
314 fibril_mutex_lock(&devices_mutex);
315 if (list_first(&devices) != NULL) {
316 /* Devices exist, roll back */
317 fibril_mutex_unlock(&devices_mutex);
318 stopping = false;
319 fibril_rwlock_write_unlock(&stopping_lock);
320 async_answer_0(icall, EBUSY);
321 return;
322 }
323
324 fibril_rwlock_write_unlock(&stopping_lock);
325
326 /* There should be no functions at this point */
327 fibril_mutex_lock(&functions_mutex);
328 assert(list_first(&functions) == NULL);
329 fibril_mutex_unlock(&functions_mutex);
330
331 /* Reply with success and terminate */
332 async_answer_0(icall, EOK);
333 exit(0);
334}
335
336static void driver_connection_devman(ipc_call_t *icall, void *arg)
337{
338 /* Accept connection */
339 async_accept_0(icall);
340
341 while (true) {
342 ipc_call_t call;
343 async_get_call(&call);
344
345 if (!ipc_get_imethod(&call)) {
346 async_answer_0(&call, EOK);
347 break;
348 }
349
350 switch (ipc_get_imethod(&call)) {
351 case DRIVER_DEV_ADD:
352 driver_dev_add(&call);
353 break;
354 case DRIVER_DEV_REMOVE:
355 driver_dev_remove(&call);
356 break;
357 case DRIVER_DEV_GONE:
358 driver_dev_gone(&call);
359 break;
360 case DRIVER_FUN_ONLINE:
361 driver_fun_online(&call);
362 break;
363 case DRIVER_FUN_OFFLINE:
364 driver_fun_offline(&call);
365 break;
366 case DRIVER_STOP:
367 driver_stop(&call);
368 break;
369 default:
370 async_answer_0(&call, ENOTSUP);
371 }
372 }
373}
374
375/** Generic client connection handler both for applications and drivers.
376 *
377 * @param drv True for driver client, false for other clients
378 * (applications, services, etc.).
379 *
380 */
381static void driver_connection_gen(ipc_call_t *icall, bool drv)
382{
383 /*
384 * Answer the first IPC_M_CONNECT_ME_TO call and remember the handle of
385 * the device to which the client connected.
386 */
387 devman_handle_t handle = ipc_get_arg2(icall);
388
389 fibril_mutex_lock(&functions_mutex);
390 ddf_fun_t *fun = driver_get_function(handle);
391 if (fun != NULL)
392 fun_add_ref(fun);
393 fibril_mutex_unlock(&functions_mutex);
394
395 if (fun == NULL) {
396 printf("%s: driver_connection_gen error - no function with handle"
397 " %" PRIun " was found.\n", driver->name, handle);
398 async_answer_0(icall, ENOENT);
399 return;
400 }
401
402 if (fun->conn_handler != NULL) {
403 /* Driver has a custom connection handler. */
404 (*fun->conn_handler)(icall, (void *)fun);
405 fun_del_ref(fun);
406 return;
407 }
408
409 /*
410 * TODO - if the client is not a driver, check whether it is allowed to
411 * use the device.
412 */
413
414 errno_t ret = EOK;
415 /* Open device function */
416 if (fun->ops != NULL && fun->ops->open != NULL)
417 ret = (*fun->ops->open)(fun);
418
419 if (ret != EOK) {
420 async_answer_0(icall, ret);
421 fun_del_ref(fun);
422 return;
423 }
424
425 async_accept_0(icall);
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 refcount_init(&dev->refcnt);
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 refcount_init(&fun->refcnt);
546 init_match_ids(&fun->match_ids);
547 link_initialize(&fun->link);
548
549 return fun;
550}
551
552/** Delete device structure.
553 *
554 * @param dev The device structure.
555 */
556static void delete_device(ddf_dev_t *dev)
557{
558 if (dev->parent_sess)
559 async_hangup(dev->parent_sess);
560 if (dev->driver_data != NULL)
561 free(dev->driver_data);
562 if (dev->name)
563 free(dev->name);
564 free(dev);
565}
566
567/** Delete function structure.
568 *
569 * @param dev The device structure.
570 */
571static void delete_function(ddf_fun_t *fun)
572{
573 clean_match_ids(&fun->match_ids);
574 if (fun->driver_data != NULL)
575 free(fun->driver_data);
576 if (fun->name != NULL)
577 free(fun->name);
578 free(fun);
579}
580
581/** Increase device reference count. */
582static void dev_add_ref(ddf_dev_t *dev)
583{
584 refcount_up(&dev->refcnt);
585}
586
587/** Decrease device reference count.
588 *
589 * Free the device structure if the reference count drops to zero.
590 */
591static void dev_del_ref(ddf_dev_t *dev)
592{
593 if (refcount_down(&dev->refcnt))
594 delete_device(dev);
595}
596
597/** Increase function reference count.
598 *
599 * This also increases reference count on the device. The device structure
600 * will thus not be deallocated while there are some associated function
601 * structures.
602 */
603static void fun_add_ref(ddf_fun_t *fun)
604{
605 dev_add_ref(fun->dev);
606 refcount_up(&fun->refcnt);
607}
608
609/** Decrease function reference count.
610 *
611 * Free the function structure if the reference count drops to zero.
612 */
613static void fun_del_ref(ddf_fun_t *fun)
614{
615 ddf_dev_t *dev = fun->dev;
616
617 if (refcount_down(&fun->refcnt))
618 delete_function(fun);
619
620 dev_del_ref(dev);
621}
622
623/** Allocate driver-specific device data. */
624void *ddf_dev_data_alloc(ddf_dev_t *dev, size_t size)
625{
626 assert(dev->driver_data == NULL);
627
628 void *data = calloc(1, size);
629 if (data == NULL)
630 return NULL;
631
632 dev->driver_data = data;
633 return data;
634}
635
636/** Return driver-specific device data. */
637void *ddf_dev_data_get(ddf_dev_t *dev)
638{
639 return dev->driver_data;
640}
641
642/** Get device handle. */
643devman_handle_t ddf_dev_get_handle(ddf_dev_t *dev)
644{
645 return dev->handle;
646}
647
648/** Return device name.
649 *
650 * @param dev Device
651 * @return Device name. Valid as long as @a dev is valid.
652 */
653const char *ddf_dev_get_name(ddf_dev_t *dev)
654{
655 return dev->name;
656}
657
658/** Return existing session with the parent function.
659 *
660 * @param dev Device
661 * @return Session with parent function or NULL upon failure
662 */
663async_sess_t *ddf_dev_parent_sess_get(ddf_dev_t *dev)
664{
665 if (dev->parent_sess == NULL) {
666 dev->parent_sess = devman_parent_device_connect(dev->handle,
667 IPC_FLAG_BLOCKING);
668 }
669
670 return dev->parent_sess;
671}
672
673/** Set function name (if it was not specified when node was created.)
674 *
675 * @param dev Device whose name has not been set yet
676 * @param name Name, will be copied
677 * @return EOK on success, ENOMEM if out of memory
678 */
679errno_t ddf_fun_set_name(ddf_fun_t *dev, const char *name)
680{
681 assert(dev->name == NULL);
682
683 dev->name = str_dup(name);
684 if (dev->name == NULL)
685 return ENOENT;
686
687 return EOK;
688}
689
690/** Get device to which function belongs. */
691ddf_dev_t *ddf_fun_get_dev(ddf_fun_t *fun)
692{
693 return fun->dev;
694}
695
696/** Get function handle.
697 *
698 * XXX USB uses this, but its use should be eliminated.
699 */
700devman_handle_t ddf_fun_get_handle(ddf_fun_t *fun)
701{
702 return fun->handle;
703}
704
705/** Create a DDF function node.
706 *
707 * Create a DDF function (in memory). Both child devices and external clients
708 * communicate with a device via its functions.
709 *
710 * The created function node is fully formed, but only exists in the memory
711 * of the client task. In order to be visible to the system, the function
712 * must be bound using ddf_fun_bind().
713 *
714 * This function should only fail if there is not enough free memory.
715 * Specifically, this function succeeds even if @a dev already has
716 * a (bound) function with the same name. @a name can be NULL in which
717 * case the caller will set the name later using ddf_fun_set_name().
718 * He must do this before binding the function.
719 *
720 * Type: A function of type fun_inner indicates that DDF should attempt
721 * to attach child devices to the function. fun_exposed means that
722 * the function should be exported to external clients (applications).
723 *
724 * @param dev Device to which we are adding function
725 * @param ftype Type of function (fun_inner or fun_exposed)
726 * @param name Name of function or NULL
727 *
728 * @return New function or @c NULL if memory is not available
729 */
730ddf_fun_t *ddf_fun_create(ddf_dev_t *dev, fun_type_t ftype, const char *name)
731{
732 ddf_fun_t *fun = create_function();
733 if (fun == NULL)
734 return NULL;
735
736 fun->dev = dev;
737 dev_add_ref(fun->dev);
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 fun_del_ref(fun); /* fun is destroyed */
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
973/** Wait for function to enter stable state.
974 *
975 * @param fun Function
976 * @return EOK on success or an error code
977 */
978errno_t ddf_fun_wait_stable(ddf_fun_t *fun)
979{
980 return devman_drv_fun_wait_stable(fun->handle);
981}
982
983errno_t ddf_driver_main(const driver_t *drv)
984{
985 /*
986 * Remember the driver structure - driver_ops will be called by generic
987 * handler for incoming connections.
988 */
989 driver = drv;
990
991 /*
992 * Register driver with device manager using generic handler for
993 * incoming connections.
994 */
995 port_id_t port;
996 errno_t rc = async_create_port(INTERFACE_DDF_DRIVER, driver_connection_driver,
997 NULL, &port);
998 if (rc != EOK) {
999 printf("Error: Failed to create driver port.\n");
1000 return rc;
1001 }
1002
1003 rc = async_create_port(INTERFACE_DDF_DEVMAN, driver_connection_devman,
1004 NULL, &port);
1005 if (rc != EOK) {
1006 printf("Error: Failed to create devman port.\n");
1007 return rc;
1008 }
1009
1010 async_set_fallback_port_handler(driver_connection_client, NULL);
1011
1012 rc = devman_driver_register(driver->name);
1013 if (rc != EOK) {
1014 printf("Error: Failed to register driver with device manager "
1015 "(%s).\n", (rc == EEXIST) ? "driver already started" :
1016 str_error(rc));
1017
1018 return rc;
1019 }
1020
1021 /* Return success from the task since server has started. */
1022 rc = task_retval(0);
1023 if (rc != EOK) {
1024 printf("Error: Failed returning task value.\n");
1025 return rc;
1026 }
1027
1028 async_manager();
1029
1030 /* Never reached. */
1031 return EOK;
1032}
1033
1034/**
1035 * @}
1036 */
Note: See TracBrowser for help on using the repository browser.