source: mainline/uspace/srv/devman/drv_conn.c@ 4d30c475

Last change on this file since 4d30c475 was 832cbe7, checked in by Jiri Svoboda <jiri@…>, 7 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: 17.3 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 devman
32 * @{
33 */
34
35/** @file
36 */
37
38#include <assert.h>
39#include <ipc/services.h>
40#include <ns.h>
41#include <async.h>
42#include <stdio.h>
43#include <errno.h>
44#include <str_error.h>
45#include <stdbool.h>
46#include <fibril_synch.h>
47#include <stdlib.h>
48#include <str.h>
49#include <io/log.h>
50#include <ipc/devman.h>
51#include <loc.h>
52
53#include "client_conn.h"
54#include "dev.h"
55#include "devman.h"
56#include "devtree.h"
57#include "driver.h"
58#include "drv_conn.h"
59#include "fun.h"
60#include "loc.h"
61#include "main.h"
62
63static errno_t init_running_drv(void *drv);
64
65/** Register running driver. */
66static driver_t *devman_driver_register(ipc_call_t *call)
67{
68 driver_t *driver = NULL;
69 char *drv_name = NULL;
70
71 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_driver_register");
72
73 /* Get driver name. */
74 errno_t rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
75 if (rc != EOK) {
76 async_answer_0(call, rc);
77 return NULL;
78 }
79
80 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s' driver is trying to register.",
81 drv_name);
82
83 /* Find driver structure. */
84 driver = driver_find_by_name(&drivers_list, drv_name);
85 if (driver == NULL) {
86 log_msg(LOG_DEFAULT, LVL_ERROR, "No driver named `%s' was found.", drv_name);
87 free(drv_name);
88 drv_name = NULL;
89 async_answer_0(call, ENOENT);
90 return NULL;
91 }
92
93 free(drv_name);
94 drv_name = NULL;
95
96 fibril_mutex_lock(&driver->driver_mutex);
97
98 if (driver->sess) {
99 /* We already have a connection to the driver. */
100 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver '%s' already started.\n",
101 driver->name);
102 fibril_mutex_unlock(&driver->driver_mutex);
103 async_answer_0(call, EEXIST);
104 return NULL;
105 }
106
107 switch (driver->state) {
108 case DRIVER_NOT_STARTED:
109 /* Somebody started the driver manually. */
110 log_msg(LOG_DEFAULT, LVL_NOTE, "Driver '%s' started manually.\n",
111 driver->name);
112 driver->state = DRIVER_STARTING;
113 break;
114 case DRIVER_STARTING:
115 /* The expected case */
116 break;
117 case DRIVER_RUNNING:
118 /* Should not happen since we do not have a connected session */
119 assert(false);
120 }
121
122 /* Create connection to the driver. */
123 log_msg(LOG_DEFAULT, LVL_DEBUG, "Creating connection to the `%s' driver.",
124 driver->name);
125 driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
126 if (!driver->sess) {
127 fibril_mutex_unlock(&driver->driver_mutex);
128 async_answer_0(call, ENOTSUP);
129 return NULL;
130 }
131 /* FIXME: Work around problem with callback sessions */
132 async_sess_args_set(driver->sess, INTERFACE_DDF_DEVMAN, 0, 0);
133
134 log_msg(LOG_DEFAULT, LVL_NOTE,
135 "The `%s' driver was successfully registered as running.",
136 driver->name);
137
138 /*
139 * Initialize the driver as running (e.g. pass assigned devices to it)
140 * in a separate fibril; the separate fibril is used to enable the
141 * driver to use devman service during the driver's initialization.
142 */
143 fid_t fid = fibril_create(init_running_drv, driver);
144 if (fid == 0) {
145 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to create initialization fibril "
146 "for driver `%s'.", driver->name);
147 fibril_mutex_unlock(&driver->driver_mutex);
148 async_answer_0(call, ENOMEM);
149 return NULL;
150 }
151
152 fibril_add_ready(fid);
153 fibril_mutex_unlock(&driver->driver_mutex);
154
155 async_answer_0(call, EOK);
156 return driver;
157}
158
159/** Receive device match ID from the device's parent driver and add it to the
160 * list of devices match ids.
161 *
162 * @param match_ids The list of the device's match ids.
163 * @return Zero on success, error code otherwise.
164 */
165static errno_t devman_receive_match_id(match_id_list_t *match_ids)
166{
167 match_id_t *match_id = create_match_id();
168 ipc_call_t call;
169 errno_t rc = 0;
170
171 async_get_call(&call);
172 if (DEVMAN_ADD_MATCH_ID != ipc_get_imethod(&call)) {
173 log_msg(LOG_DEFAULT, LVL_ERROR,
174 "Invalid protocol when trying to receive match id.");
175 async_answer_0(&call, EINVAL);
176 delete_match_id(match_id);
177 return EINVAL;
178 }
179
180 if (match_id == NULL) {
181 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate match id.");
182 async_answer_0(&call, ENOMEM);
183 return ENOMEM;
184 }
185
186 async_answer_0(&call, EOK);
187
188 match_id->score = ipc_get_arg1(&call);
189
190 char *match_id_str;
191 rc = async_data_write_accept((void **) &match_id_str, true, 0, 0, 0, 0);
192 match_id->id = match_id_str;
193 if (rc != EOK) {
194 delete_match_id(match_id);
195 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to receive match id string: %s.",
196 str_error(rc));
197 return rc;
198 }
199
200 list_append(&match_id->link, &match_ids->ids);
201
202 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received match id `%s', score %d.",
203 match_id->id, match_id->score);
204 return rc;
205}
206
207/** Receive device match IDs from the device's parent driver and add them to the
208 * list of devices match ids.
209 *
210 * @param match_count The number of device's match ids to be received.
211 * @param match_ids The list of the device's match ids.
212 * @return Zero on success, error code otherwise.
213 */
214static errno_t devman_receive_match_ids(sysarg_t match_count,
215 match_id_list_t *match_ids)
216{
217 errno_t ret = EOK;
218 size_t i;
219
220 for (i = 0; i < match_count; i++) {
221 if (EOK != (ret = devman_receive_match_id(match_ids)))
222 return ret;
223 }
224 return ret;
225}
226
227/** Handle function registration.
228 *
229 * Child devices are registered by their parent's device driver.
230 */
231static void devman_add_function(ipc_call_t *call)
232{
233 fun_type_t ftype = (fun_type_t) ipc_get_arg1(call);
234 devman_handle_t dev_handle = ipc_get_arg2(call);
235 sysarg_t match_count = ipc_get_arg3(call);
236 dev_tree_t *tree = &device_tree;
237
238 dev_node_t *pdev = find_dev_node(&device_tree, dev_handle);
239 if (pdev == NULL) {
240 async_answer_0(call, ENOENT);
241 return;
242 }
243
244 if (ftype != fun_inner && ftype != fun_exposed) {
245 /* Unknown function type */
246 log_msg(LOG_DEFAULT, LVL_ERROR,
247 "Unknown function type %d provided by driver.",
248 (int) ftype);
249
250 dev_del_ref(pdev);
251 async_answer_0(call, EINVAL);
252 return;
253 }
254
255 char *fun_name = NULL;
256 errno_t rc = async_data_write_accept((void **) &fun_name, true, 0, 0, 0, 0);
257 if (rc != EOK) {
258 dev_del_ref(pdev);
259 async_answer_0(call, rc);
260 return;
261 }
262
263 fibril_rwlock_write_lock(&tree->rwlock);
264
265 /* Check device state */
266 if (pdev->state == DEVICE_REMOVED) {
267 fibril_rwlock_write_unlock(&tree->rwlock);
268 dev_del_ref(pdev);
269 async_answer_0(call, ENOENT);
270 return;
271 }
272
273 /* Check that function with same name is not there already. */
274 fun_node_t *tfun = find_fun_node_in_device(tree, pdev, fun_name);
275 if (tfun) {
276 fun_del_ref(tfun); /* drop the new unwanted reference */
277 fibril_rwlock_write_unlock(&tree->rwlock);
278 dev_del_ref(pdev);
279 async_answer_0(call, EEXIST);
280 printf(NAME ": Warning, driver tried to register `%s' twice.\n",
281 fun_name);
282 free(fun_name);
283 return;
284 }
285
286 fun_node_t *fun = create_fun_node();
287 /*
288 * Hold a temporary reference while we work with fun. The reference from
289 * create_fun_node() moves to the device tree.
290 */
291 fun_add_ref(fun);
292 fun->ftype = ftype;
293
294 /*
295 * We can lock the function here even when holding the tree because
296 * we know it cannot be held by anyone else yet.
297 */
298 fun_busy_lock(fun);
299
300 if (!insert_fun_node(&device_tree, fun, fun_name, pdev)) {
301 fibril_rwlock_write_unlock(&tree->rwlock);
302 dev_del_ref(pdev);
303 fun_busy_unlock(fun);
304 fun_del_ref(fun);
305 fun_del_ref(fun); /* fun is destroyed */
306 async_answer_0(call, ENOMEM);
307 return;
308 }
309
310 fibril_rwlock_write_unlock(&tree->rwlock);
311 dev_del_ref(pdev);
312
313 devman_receive_match_ids(match_count, &fun->match_ids);
314
315 rc = fun_online(fun);
316 if (rc != EOK) {
317 /* XXX Set some failed state? */
318 fun_busy_unlock(fun);
319 fun_del_ref(fun);
320 async_answer_0(call, rc);
321 return;
322 }
323
324 fun_busy_unlock(fun);
325 fun_del_ref(fun);
326
327 /* Return device handle to parent's driver. */
328 async_answer_1(call, EOK, fun->handle);
329}
330
331static void devman_add_function_to_cat(ipc_call_t *call)
332{
333 devman_handle_t handle = ipc_get_arg1(call);
334 category_id_t cat_id;
335 errno_t rc;
336
337 /* Get category name. */
338 char *cat_name;
339 rc = async_data_write_accept((void **) &cat_name, true,
340 0, 0, 0, 0);
341 if (rc != EOK) {
342 async_answer_0(call, rc);
343 return;
344 }
345
346 fun_node_t *fun = find_fun_node(&device_tree, handle);
347 if (fun == NULL) {
348 async_answer_0(call, ENOENT);
349 return;
350 }
351
352 fibril_rwlock_read_lock(&device_tree.rwlock);
353
354 /* Check function state */
355 if (fun->state == FUN_REMOVED) {
356 fibril_rwlock_read_unlock(&device_tree.rwlock);
357 async_answer_0(call, ENOENT);
358 return;
359 }
360
361 rc = loc_category_get_id(cat_name, &cat_id, IPC_FLAG_BLOCKING);
362 if (rc == EOK)
363 rc = loc_service_add_to_cat(devman_srv, fun->service_id, cat_id);
364 if (rc == EOK) {
365 log_msg(LOG_DEFAULT, LVL_NOTE, "Function `%s' added to category `%s'.",
366 fun->pathname, cat_name);
367 } else {
368 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed adding function `%s' to category "
369 "`%s'.", fun->pathname, cat_name);
370 }
371
372 fibril_rwlock_read_unlock(&device_tree.rwlock);
373 fun_del_ref(fun);
374
375 async_answer_0(call, rc);
376}
377
378/** Online function by driver request.
379 *
380 */
381static void devman_drv_fun_online(ipc_call_t *icall, driver_t *drv)
382{
383 fun_node_t *fun;
384 errno_t rc;
385
386 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_drv_fun_online()");
387
388 fun = find_fun_node(&device_tree, ipc_get_arg1(icall));
389 if (fun == NULL) {
390 async_answer_0(icall, ENOENT);
391 return;
392 }
393
394 fun_busy_lock(fun);
395
396 fibril_rwlock_read_lock(&device_tree.rwlock);
397 if (fun->dev == NULL || fun->dev->drv != drv) {
398 fibril_rwlock_read_unlock(&device_tree.rwlock);
399 fun_busy_unlock(fun);
400 fun_del_ref(fun);
401 async_answer_0(icall, ENOENT);
402 return;
403 }
404 fibril_rwlock_read_unlock(&device_tree.rwlock);
405
406 rc = fun_online(fun);
407 if (rc != EOK) {
408 fun_busy_unlock(fun);
409 fun_del_ref(fun);
410 async_answer_0(icall, rc);
411 return;
412 }
413
414 fun_busy_unlock(fun);
415 fun_del_ref(fun);
416
417 async_answer_0(icall, EOK);
418}
419
420/** Offline function by driver request.
421 *
422 */
423static void devman_drv_fun_offline(ipc_call_t *icall, driver_t *drv)
424{
425 fun_node_t *fun;
426 errno_t rc;
427
428 fun = find_fun_node(&device_tree, ipc_get_arg1(icall));
429 if (fun == NULL) {
430 async_answer_0(icall, ENOENT);
431 return;
432 }
433
434 fun_busy_lock(fun);
435
436 fibril_rwlock_write_lock(&device_tree.rwlock);
437 if (fun->dev == NULL || fun->dev->drv != drv) {
438 fun_busy_unlock(fun);
439 fun_del_ref(fun);
440 async_answer_0(icall, ENOENT);
441 return;
442 }
443 fibril_rwlock_write_unlock(&device_tree.rwlock);
444
445 rc = fun_offline(fun);
446 if (rc != EOK) {
447 fun_busy_unlock(fun);
448 fun_del_ref(fun);
449 async_answer_0(icall, rc);
450 return;
451 }
452
453 fun_busy_unlock(fun);
454 fun_del_ref(fun);
455 async_answer_0(icall, EOK);
456}
457
458/** Wait for function to become stable.
459 *
460 */
461static void devman_drv_fun_wait_stable(ipc_call_t *icall, driver_t *drv)
462{
463 fun_node_t *fun;
464 dev_node_t *dev;
465
466 fibril_rwlock_read_lock(&device_tree.rwlock);
467
468 fun = find_fun_node(&device_tree, ipc_get_arg1(icall));
469 if (fun == NULL) {
470 fibril_rwlock_read_unlock(&device_tree.rwlock);
471 async_answer_0(icall, ENOENT);
472 return;
473 }
474
475 if (fun->child == NULL) {
476 fibril_rwlock_read_unlock(&device_tree.rwlock);
477 fun_del_ref(fun);
478 async_answer_0(icall, EOK);
479 return;
480 }
481
482 dev = fun->child;
483 dev_add_ref(dev);
484
485 fibril_rwlock_read_unlock(&device_tree.rwlock);
486
487 dev_wait_stable(dev);
488 dev_del_ref(dev);
489
490 async_answer_0(icall, EOK);
491}
492
493/** Remove function. */
494static void devman_remove_function(ipc_call_t *call)
495{
496 devman_handle_t fun_handle = ipc_get_arg1(call);
497 dev_tree_t *tree = &device_tree;
498 errno_t rc;
499
500 fun_node_t *fun = find_fun_node(&device_tree, fun_handle);
501 if (fun == NULL) {
502 async_answer_0(call, ENOENT);
503 return;
504 }
505
506 fun_busy_lock(fun);
507
508 fibril_rwlock_write_lock(&tree->rwlock);
509
510 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function(fun='%s')", fun->pathname);
511
512 /* Check function state */
513 if (fun->state == FUN_REMOVED) {
514 fibril_rwlock_write_unlock(&tree->rwlock);
515 fun_busy_unlock(fun);
516 fun_del_ref(fun);
517 async_answer_0(call, ENOENT);
518 return;
519 }
520
521 if (fun->ftype == fun_inner) {
522 /* This is a surprise removal. Handle possible descendants */
523 if (fun->child != NULL) {
524 dev_node_t *dev = fun->child;
525 device_state_t dev_state;
526 errno_t gone_rc;
527
528 dev_add_ref(dev);
529 dev_state = dev->state;
530
531 fibril_rwlock_write_unlock(&device_tree.rwlock);
532
533 /* If device is owned by driver, inform driver it is gone. */
534 if (dev_state == DEVICE_USABLE)
535 gone_rc = driver_dev_gone(&device_tree, dev);
536 else
537 gone_rc = EOK;
538
539 fibril_rwlock_read_lock(&device_tree.rwlock);
540
541 /* Verify that driver succeeded and removed all functions */
542 if (gone_rc != EOK || !list_empty(&dev->functions)) {
543 log_msg(LOG_DEFAULT, LVL_ERROR, "Driver did not remove "
544 "functions for device that is gone. "
545 "Device node is now defunct.");
546
547 /*
548 * Not much we can do but mark the device
549 * node as having invalid state. This
550 * is a driver bug.
551 */
552 dev->state = DEVICE_INVALID;
553 fibril_rwlock_read_unlock(&device_tree.rwlock);
554 dev_del_ref(dev);
555 if (gone_rc == EOK)
556 gone_rc = ENOTSUP;
557 fun_busy_unlock(fun);
558 fun_del_ref(fun);
559 async_answer_0(call, gone_rc);
560 return;
561 }
562
563 driver_t *driver = dev->drv;
564 fibril_rwlock_read_unlock(&device_tree.rwlock);
565
566 if (driver)
567 detach_driver(&device_tree, dev);
568
569 fibril_rwlock_write_lock(&device_tree.rwlock);
570 remove_dev_node(&device_tree, dev);
571
572 /* Delete ref created when node was inserted */
573 dev_del_ref(dev);
574 /* Delete ref created by dev_add_ref(dev) above */
575 dev_del_ref(dev);
576 }
577 } else {
578 if (fun->service_id != 0) {
579 /* Unregister from location service */
580 rc = loc_unregister_tree_function(fun, &device_tree);
581 if (rc != EOK) {
582 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unregistering tree "
583 "service.");
584 fibril_rwlock_write_unlock(&tree->rwlock);
585 fun_busy_unlock(fun);
586 fun_del_ref(fun);
587 async_answer_0(call, EIO);
588 return;
589 }
590 }
591 }
592
593 remove_fun_node(&device_tree, fun);
594 fibril_rwlock_write_unlock(&tree->rwlock);
595 fun_busy_unlock(fun);
596
597 /* Delete ref added when inserting function into tree */
598 fun_del_ref(fun);
599 /* Delete ref added above when looking up function */
600 fun_del_ref(fun);
601
602 log_msg(LOG_DEFAULT, LVL_DEBUG, "devman_remove_function() succeeded.");
603 async_answer_0(call, EOK);
604}
605
606/** Initialize driver which has registered itself as running and ready.
607 *
608 * The initialization is done in a separate fibril to avoid deadlocks (if the
609 * driver needed to be served by devman during the driver's initialization).
610 */
611static errno_t init_running_drv(void *drv)
612{
613 driver_t *driver = (driver_t *) drv;
614
615 initialize_running_driver(driver, &device_tree);
616 log_msg(LOG_DEFAULT, LVL_DEBUG, "The `%s` driver was successfully initialized.",
617 driver->name);
618 return 0;
619}
620
621/** Function for handling connections from a driver to the device manager. */
622void devman_connection_driver(ipc_call_t *icall, void *arg)
623{
624 client_t *client;
625 driver_t *driver = NULL;
626
627 /* Accept the connection. */
628 async_accept_0(icall);
629
630 client = async_get_client_data();
631 if (client == NULL) {
632 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to allocate client data.");
633 return;
634 }
635
636 while (true) {
637 ipc_call_t call;
638 async_get_call(&call);
639
640 if (!ipc_get_imethod(&call)) {
641 async_answer_0(&call, EOK);
642 break;
643 }
644
645 if (ipc_get_imethod(&call) != DEVMAN_DRIVER_REGISTER) {
646 fibril_mutex_lock(&client->mutex);
647 driver = client->driver;
648 fibril_mutex_unlock(&client->mutex);
649 if (driver == NULL) {
650 /* First call must be to DEVMAN_DRIVER_REGISTER */
651 async_answer_0(&call, ENOTSUP);
652 continue;
653 }
654 }
655
656 switch (ipc_get_imethod(&call)) {
657 case DEVMAN_DRIVER_REGISTER:
658 fibril_mutex_lock(&client->mutex);
659 if (client->driver != NULL) {
660 fibril_mutex_unlock(&client->mutex);
661 async_answer_0(&call, EINVAL);
662 continue;
663 }
664 client->driver = devman_driver_register(&call);
665 fibril_mutex_unlock(&client->mutex);
666 break;
667 case DEVMAN_ADD_FUNCTION:
668 devman_add_function(&call);
669 break;
670 case DEVMAN_ADD_DEVICE_TO_CATEGORY:
671 devman_add_function_to_cat(&call);
672 break;
673 case DEVMAN_DRV_FUN_ONLINE:
674 devman_drv_fun_online(&call, driver);
675 break;
676 case DEVMAN_DRV_FUN_OFFLINE:
677 devman_drv_fun_offline(&call, driver);
678 break;
679 case DEVMAN_DRV_FUN_WAIT_STABLE:
680 devman_drv_fun_wait_stable(&call, driver);
681 break;
682 case DEVMAN_REMOVE_FUNCTION:
683 devman_remove_function(&call);
684 break;
685 default:
686 async_answer_0(&call, EINVAL);
687 break;
688 }
689 }
690}
691
692/** @}
693 */
Note: See TracBrowser for help on using the repository browser.