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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 17aca1c was 17aca1c, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Merge mainline changes

  • Property mode set to 100644
File size: 14.2 KB
RevLine 
[c16cf62]1/*
2 * Copyright (c) 2010 Lenka Trochtova
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/**
30 * @defgroup libdrv generic device driver support.
31 * @brief HelenOS generic device driver support.
32 * @{
33 */
34
35/** @file
36 */
[52b7b1bb]37
[c16cf62]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 <bool.h>
45#include <fibril_synch.h>
46#include <stdlib.h>
[c47e1a8]47#include <str.h>
[c16cf62]48#include <ctype.h>
[66babbd]49#include <errno.h>
[7e752b2]50#include <inttypes.h>
[c16cf62]51
[084ff99]52#include <ipc/driver.h>
[c16cf62]53
[5fdd7c3]54#include "dev_iface.h"
[c16cf62]55#include "driver.h"
56
[36f2b3e]57/** Driver structure */
[c16cf62]58static driver_t *driver;
[7f8b581]59
[36f2b3e]60/** Devices */
[084ff99]61LIST_INITIALIZE(devices);
[9a66bc2e]62FIBRIL_MUTEX_INITIALIZE(devices_mutex);
[084ff99]63
[36f2b3e]64/** Interrupts */
[7f8b581]65static interrupt_context_list_t interrupt_contexts;
66
67static irq_cmd_t default_cmds[] = {
68 {
69 .cmd = CMD_ACCEPT
70 }
71};
72
73static irq_code_t default_pseudocode = {
74 sizeof(default_cmds) / sizeof(irq_cmd_t),
75 default_cmds
76};
77
78
79static void driver_irq_handler(ipc_callid_t iid, ipc_call_t *icall)
[7a252ec8]80{
[228e490]81 int id = (int)IPC_GET_IMETHOD(*icall);
[7a252ec8]82 interrupt_context_t *ctx;
83
84 ctx = find_interrupt_context_by_id(&interrupt_contexts, id);
[36f2b3e]85 if (ctx != NULL && ctx->handler != NULL)
[7a252ec8]86 (*ctx->handler)(ctx->dev, iid, icall);
[7f8b581]87}
88
[5fdd7c3]89interrupt_context_t *create_interrupt_context(void)
90{
91 interrupt_context_t *ctx;
92
93 ctx = (interrupt_context_t *) malloc(sizeof(interrupt_context_t));
94 if (ctx != NULL)
95 memset(ctx, 0, sizeof(interrupt_context_t));
96
97 return ctx;
98}
99
100void delete_interrupt_context(interrupt_context_t *ctx)
101{
102 if (ctx != NULL)
103 free(ctx);
104}
105
106void init_interrupt_context_list(interrupt_context_list_t *list)
107{
108 memset(list, 0, sizeof(interrupt_context_list_t));
109 fibril_mutex_initialize(&list->mutex);
110 list_initialize(&list->contexts);
111}
112
113void
114add_interrupt_context(interrupt_context_list_t *list, interrupt_context_t *ctx)
115{
116 fibril_mutex_lock(&list->mutex);
117 ctx->id = list->curr_id++;
118 list_append(&ctx->link, &list->contexts);
119 fibril_mutex_unlock(&list->mutex);
120}
121
122void remove_interrupt_context(interrupt_context_list_t *list,
123 interrupt_context_t *ctx)
124{
125 fibril_mutex_lock(&list->mutex);
126 list_remove(&ctx->link);
127 fibril_mutex_unlock(&list->mutex);
128}
129
130interrupt_context_t *
131find_interrupt_context_by_id(interrupt_context_list_t *list, int id)
132{
133 fibril_mutex_lock(&list->mutex);
134
135 link_t *link = list->contexts.next;
136 interrupt_context_t *ctx;
137
138 while (link != &list->contexts) {
139 ctx = list_get_instance(link, interrupt_context_t, link);
140 if (ctx->id == id) {
141 fibril_mutex_unlock(&list->mutex);
142 return ctx;
143 }
144 link = link->next;
145 }
146
147 fibril_mutex_unlock(&list->mutex);
148 return NULL;
149}
150
151interrupt_context_t *
152find_interrupt_context(interrupt_context_list_t *list, device_t *dev, int irq)
153{
154 fibril_mutex_lock(&list->mutex);
155
156 link_t *link = list->contexts.next;
157 interrupt_context_t *ctx;
158
159 while (link != &list->contexts) {
160 ctx = list_get_instance(link, interrupt_context_t, link);
161 if (ctx->irq == irq && ctx->dev == dev) {
162 fibril_mutex_unlock(&list->mutex);
163 return ctx;
164 }
165 link = link->next;
166 }
167
168 fibril_mutex_unlock(&list->mutex);
169 return NULL;
170}
171
172
[7a252ec8]173int
174register_interrupt_handler(device_t *dev, int irq, interrupt_handler_t *handler,
175 irq_code_t *pseudocode)
[7f8b581]176{
177 interrupt_context_t *ctx = create_interrupt_context();
178
179 ctx->dev = dev;
180 ctx->irq = irq;
181 ctx->handler = handler;
182
183 add_interrupt_context(&interrupt_contexts, ctx);
184
[36f2b3e]185 if (pseudocode == NULL)
[7f8b581]186 pseudocode = &default_pseudocode;
187
[ffa2c8ef]188 int res = register_irq(irq, dev->handle, ctx->id, pseudocode);
[36f2b3e]189 if (res != EOK) {
[7f8b581]190 remove_interrupt_context(&interrupt_contexts, ctx);
191 delete_interrupt_context(ctx);
192 }
[7a252ec8]193
194 return res;
[7f8b581]195}
196
197int unregister_interrupt_handler(device_t *dev, int irq)
198{
[7a252ec8]199 interrupt_context_t *ctx = find_interrupt_context(&interrupt_contexts,
200 dev, irq);
[ffa2c8ef]201 int res = unregister_irq(irq, dev->handle);
[36f2b3e]202
203 if (ctx != NULL) {
[7f8b581]204 remove_interrupt_context(&interrupt_contexts, ctx);
[7a252ec8]205 delete_interrupt_context(ctx);
[7f8b581]206 }
[36f2b3e]207
[7f8b581]208 return res;
209}
210
[9a66bc2e]211static void add_to_devices_list(device_t *dev)
212{
213 fibril_mutex_lock(&devices_mutex);
214 list_append(&dev->link, &devices);
215 fibril_mutex_unlock(&devices_mutex);
216}
217
218static void remove_from_devices_list(device_t *dev)
219{
220 fibril_mutex_lock(&devices_mutex);
[5af21c5]221 list_remove(&dev->link);
[9a66bc2e]222 fibril_mutex_unlock(&devices_mutex);
223}
224
[d35ac1d]225static device_t *driver_get_device(link_t *devices, devman_handle_t handle)
[52b7b1bb]226{
[a1769ee]227 device_t *dev = NULL;
[9a66bc2e]228
[7a252ec8]229 fibril_mutex_lock(&devices_mutex);
[a1769ee]230 link_t *link = devices->next;
[36f2b3e]231
[a1769ee]232 while (link != devices) {
233 dev = list_get_instance(link, device_t, link);
[36f2b3e]234 if (dev->handle == handle) {
[9a66bc2e]235 fibril_mutex_unlock(&devices_mutex);
[a1769ee]236 return dev;
237 }
[9a66bc2e]238 link = link->next;
[a1769ee]239 }
[36f2b3e]240
[9a66bc2e]241 fibril_mutex_unlock(&devices_mutex);
[36f2b3e]242
[a1769ee]243 return NULL;
244}
245
[52b7b1bb]246static void driver_add_device(ipc_callid_t iid, ipc_call_t *icall)
[084ff99]247{
[df747b9c]248 char *dev_name = NULL;
[36f2b3e]249 int res;
[9a66bc2e]250
[d35ac1d]251 devman_handle_t dev_handle = IPC_GET_ARG1(*icall);
[0d6915f]252 devman_handle_t parent_dev_handle = IPC_GET_ARG2(*icall);
[d35ac1d]253
[5af21c5]254 device_t *dev = create_device();
[084ff99]255 dev->handle = dev_handle;
[df747b9c]256
[7a252ec8]257 async_data_write_accept((void **) &dev_name, true, 0, 0, 0, 0);
[df747b9c]258 dev->name = dev_name;
259
[7a252ec8]260 add_to_devices_list(dev);
[0d6915f]261 dev->parent = driver_get_device(&devices, parent_dev_handle);
262
[df747b9c]263 res = driver->driver_ops->add_device(dev);
[36f2b3e]264 if (res == EOK) {
[7e752b2]265 printf("%s: new device with handle=%" PRIun " was added.\n",
[7a252ec8]266 driver->name, dev_handle);
[9a66bc2e]267 } else {
[7e752b2]268 printf("%s: failed to add a new device with handle = %" PRIun ".\n",
[7a252ec8]269 driver->name, dev_handle);
[9a66bc2e]270 remove_from_devices_list(dev);
[7a252ec8]271 delete_device(dev);
[084ff99]272 }
[df747b9c]273
[ffa2c8ef]274 async_answer_0(iid, res);
[084ff99]275}
[c16cf62]276
277static void driver_connection_devman(ipc_callid_t iid, ipc_call_t *icall)
278{
279 /* Accept connection */
[ffa2c8ef]280 async_answer_0(iid, EOK);
[36f2b3e]281
[c16cf62]282 bool cont = true;
283 while (cont) {
284 ipc_call_t call;
285 ipc_callid_t callid = async_get_call(&call);
[36f2b3e]286
[228e490]287 switch (IPC_GET_IMETHOD(call)) {
[c16cf62]288 case IPC_M_PHONE_HUNGUP:
289 cont = false;
290 continue;
291 case DRIVER_ADD_DEVICE:
[084ff99]292 driver_add_device(callid, &call);
[c16cf62]293 break;
294 default:
[ffa2c8ef]295 async_answer_0(callid, ENOENT);
[c16cf62]296 }
[52b7b1bb]297 }
[c16cf62]298}
299
[52b7b1bb]300/**
[a1769ee]301 * Generic client connection handler both for applications and drivers.
[52b7b1bb]302 *
[7a252ec8]303 * @param drv True for driver client, false for other clients
304 * (applications, services etc.).
[a1769ee]305 */
[9a66bc2e]306static void driver_connection_gen(ipc_callid_t iid, ipc_call_t *icall, bool drv)
[52b7b1bb]307{
[7a252ec8]308 /*
309 * Answer the first IPC_M_CONNECT_ME_TO call and remember the handle of
310 * the device to which the client connected.
311 */
[0b5a4131]312 devman_handle_t handle = IPC_GET_ARG2(*icall);
[a1769ee]313 device_t *dev = driver_get_device(&devices, handle);
[52b7b1bb]314
[a1769ee]315 if (dev == NULL) {
[7a252ec8]316 printf("%s: driver_connection_gen error - no device with handle"
[7e752b2]317 " %" PRIun " was found.\n", driver->name, handle);
[ffa2c8ef]318 async_answer_0(iid, ENOENT);
[a1769ee]319 return;
320 }
[5cd136ab]321
322
[7a252ec8]323 /*
324 * TODO - if the client is not a driver, check whether it is allowed to
325 * use the device.
326 */
[36f2b3e]327
[25a7e11d]328 int ret = EOK;
[7a252ec8]329 /* open the device */
[36f2b3e]330 if (dev->ops != NULL && dev->ops->open != NULL)
[5159ae9]331 ret = (*dev->ops->open)(dev);
[a1769ee]332
[ffa2c8ef]333 async_answer_0(iid, ret);
[36f2b3e]334 if (ret != EOK)
[a6e54c5d]335 return;
[36f2b3e]336
[a1769ee]337 while (1) {
338 ipc_callid_t callid;
339 ipc_call_t call;
340 callid = async_get_call(&call);
[228e490]341 sysarg_t method = IPC_GET_IMETHOD(call);
[3843ecb]342 int iface_idx;
343
[a1769ee]344 switch (method) {
[d35ac1d]345 case IPC_M_PHONE_HUNGUP:
[7a252ec8]346 /* close the device */
[36f2b3e]347 if (dev->ops != NULL && dev->ops->close != NULL)
[5159ae9]348 (*dev->ops->close)(dev);
[ffa2c8ef]349 async_answer_0(callid, EOK);
[a1769ee]350 return;
[d35ac1d]351 default:
[7a252ec8]352 /* convert ipc interface id to interface index */
[3843ecb]353
354 iface_idx = DEV_IFACE_IDX(method);
355
356 if (!is_valid_iface_idx(iface_idx)) {
[7a252ec8]357 remote_handler_t *default_handler =
358 device_get_default_handler(dev);
[36f2b3e]359 if (default_handler != NULL) {
[08d9525a]360 (*default_handler)(dev, callid, &call);
361 break;
362 }
[7a252ec8]363 /*
364 * This is not device's interface and the
365 * default handler is not provided.
366 */
367 printf("%s: driver_connection_gen error - "
368 "invalid interface id %d.",
369 driver->name, iface_idx);
[ffa2c8ef]370 async_answer_0(callid, ENOTSUP);
[52b7b1bb]371 break;
372 }
[36f2b3e]373
[7a252ec8]374 /* calling one of the device's interfaces */
[52b7b1bb]375
[d35ac1d]376 /* Get the interface ops structure. */
377 void *ops = device_get_ops(dev, iface_idx);
378 if (ops == NULL) {
[7a252ec8]379 printf("%s: driver_connection_gen error - ",
380 driver->name);
[7e752b2]381 printf("device with handle %" PRIun " has no interface "
[7a252ec8]382 "with id %d.\n", handle, iface_idx);
[ffa2c8ef]383 async_answer_0(callid, ENOTSUP);
[52b7b1bb]384 break;
[a1769ee]385 }
[36f2b3e]386
[7a252ec8]387 /*
388 * Get the corresponding interface for remote request
389 * handling ("remote interface").
390 */
[d35ac1d]391 remote_iface_t *rem_iface = get_remote_iface(iface_idx);
[36f2b3e]392 assert(rem_iface != NULL);
393
[7a252ec8]394 /* get the method of the remote interface */
[96b02eb9]395 sysarg_t iface_method_idx = IPC_GET_ARG1(call);
[7a252ec8]396 remote_iface_func_ptr_t iface_method_ptr =
397 get_remote_method(rem_iface, iface_method_idx);
[36f2b3e]398 if (iface_method_ptr == NULL) {
[52b7b1bb]399 // the interface has not such method
[7a252ec8]400 printf("%s: driver_connection_gen error - "
401 "invalid interface method.", driver->name);
[ffa2c8ef]402 async_answer_0(callid, ENOTSUP);
[52b7b1bb]403 break;
404 }
405
[7a252ec8]406 /*
407 * Call the remote interface's method, which will
408 * receive parameters from the remote client and it will
409 * pass it to the corresponding local interface method
410 * associated with the device by its driver.
411 */
[d35ac1d]412 (*iface_method_ptr)(dev, ops, callid, &call);
[a1769ee]413 break;
414 }
415 }
416}
417
[c16cf62]418static void driver_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
419{
[52b7b1bb]420 driver_connection_gen(iid, icall, true);
[c16cf62]421}
422
423static void driver_connection_client(ipc_callid_t iid, ipc_call_t *icall)
424{
[52b7b1bb]425 driver_connection_gen(iid, icall, false);
[c16cf62]426}
427
428
[7a252ec8]429/** Function for handling connections to device driver. */
[c16cf62]430static void driver_connection(ipc_callid_t iid, ipc_call_t *icall)
431{
432 /* Select interface */
[96b02eb9]433 switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
[c16cf62]434 case DRIVER_DEVMAN:
[36f2b3e]435 /* Handle request from device manager */
[c16cf62]436 driver_connection_devman(iid, icall);
437 break;
438 case DRIVER_DRIVER:
[36f2b3e]439 /* Handle request from drivers of child devices */
[c16cf62]440 driver_connection_driver(iid, icall);
441 break;
442 case DRIVER_CLIENT:
[36f2b3e]443 /* Handle request from client applications */
[c16cf62]444 driver_connection_client(iid, icall);
445 break;
446 default:
[52b7b1bb]447 /* No such interface */
[ffa2c8ef]448 async_answer_0(iid, ENOENT);
[c16cf62]449 }
450}
451
[5fdd7c3]452/** Create new device structure.
453 *
454 * @return The device structure.
455 */
456device_t *create_device(void)
457{
458 device_t *dev = malloc(sizeof(device_t));
459
460 if (dev != NULL) {
461 memset(dev, 0, sizeof(device_t));
462 init_match_ids(&dev->match_ids);
463 }
464
465 return dev;
466}
467
468/** Delete device structure.
469 *
470 * @param dev The device structure.
471 */
472void delete_device(device_t *dev)
[7707954]473{
[5fdd7c3]474 clean_match_ids(&dev->match_ids);
475 if (dev->name != NULL)
476 free(dev->name);
477 free(dev);
478}
[52b7b1bb]479
[5fdd7c3]480void *device_get_ops(device_t *dev, dev_inferface_idx_t idx)
481{
482 assert(is_valid_iface_idx(idx));
483 if (dev->ops == NULL)
484 return NULL;
485 return dev->ops->interfaces[idx];
486}
487
[df747b9c]488int child_device_register(device_t *child, device_t *parent)
[7707954]489{
[36f2b3e]490 assert(child->name != NULL);
491
[df747b9c]492 int res;
493
[9a66bc2e]494 add_to_devices_list(child);
[7a252ec8]495 res = devman_child_device_register(child->name, &child->match_ids,
496 parent->handle, &child->handle);
[36f2b3e]497 if (res != EOK) {
498 remove_from_devices_list(child);
[df747b9c]499 return res;
[36f2b3e]500 }
501
[df747b9c]502 return res;
[7707954]503}
504
[0ca16307]505/** Wrapper for child_device_register for devices with single match id.
506 *
507 * @param parent Parent device.
508 * @param child_name Child device name.
509 * @param child_match_id Child device match id.
510 * @param child_match_score Child device match score.
511 * @return Error code.
512 */
513int child_device_register_wrapper(device_t *parent, const char *child_name,
[9f6c5ef0]514 const char *child_match_id, int child_match_score,
515 devman_handle_t *child_handle)
[0ca16307]516{
517 device_t *child = NULL;
518 match_id_t *match_id = NULL;
519 int rc;
[36f2b3e]520
[0ca16307]521 child = create_device();
522 if (child == NULL) {
523 rc = ENOMEM;
524 goto failure;
525 }
[36f2b3e]526
[0ca16307]527 child->name = child_name;
[36f2b3e]528
[0ca16307]529 match_id = create_match_id();
530 if (match_id == NULL) {
531 rc = ENOMEM;
532 goto failure;
533 }
[36f2b3e]534
[0ca16307]535 match_id->id = child_match_id;
536 match_id->score = child_match_score;
537 add_match_id(&child->match_ids, match_id);
[36f2b3e]538
[0ca16307]539 rc = child_device_register(child, parent);
[36f2b3e]540 if (rc != EOK)
[0ca16307]541 goto failure;
542
[9f6c5ef0]543 if (child_handle != NULL) {
544 *child_handle = child->handle;
545 }
[0ca16307]546
[4006447]547 return EOK;
[36f2b3e]548
[0ca16307]549failure:
550 if (match_id != NULL) {
551 match_id->id = NULL;
552 delete_match_id(match_id);
553 }
[36f2b3e]554
[0ca16307]555 if (child != NULL) {
556 child->name = NULL;
557 delete_device(child);
558 }
[36f2b3e]559
[0ca16307]560 return rc;
561}
562
[5fdd7c3]563/** Get default handler for client requests */
564remote_handler_t *device_get_default_handler(device_t *dev)
565{
566 if (dev->ops == NULL)
567 return NULL;
568 return dev->ops->default_handler;
569}
570
571int add_device_to_class(device_t *dev, const char *class_name)
572{
573 return devman_add_device_to_class(dev->handle, class_name);
574}
575
[52b7b1bb]576int driver_main(driver_t *drv)
[c16cf62]577{
[7a252ec8]578 /*
579 * Remember the driver structure - driver_ops will be called by generic
580 * handler for incoming connections.
581 */
[c16cf62]582 driver = drv;
[36f2b3e]583
[7a252ec8]584 /* Initialize the list of interrupt contexts. */
[7f8b581]585 init_interrupt_context_list(&interrupt_contexts);
586
[7a252ec8]587 /* Set generic interrupt handler. */
[7f8b581]588 async_set_interrupt_received(driver_irq_handler);
589
[7a252ec8]590 /*
591 * Register driver by device manager with generic handler for incoming
592 * connections.
593 */
[52b7b1bb]594 devman_driver_register(driver->name, driver_connection);
[36f2b3e]595
[c16cf62]596 async_manager();
[36f2b3e]597
[7a252ec8]598 /* Never reached. */
[52b7b1bb]599 return 0;
[c16cf62]600}
601
602/**
603 * @}
[52b7b1bb]604 */
Note: See TracBrowser for help on using the repository browser.