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

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

unify interface API

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