source: mainline/uspace/drv/bus/isa/isa.c

Last change on this file 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: 20.5 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * Copyright (c) 2010 Lenka Trochtova
4 * Copyright (c) 2011 Jan Vesely
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @addtogroup isa
33 * @{
34 */
35
36/** @file
37 */
38
39#include <adt/list.h>
40#include <assert.h>
41#include <stdio.h>
42#include <errno.h>
43#include <stdbool.h>
44#include <fibril_synch.h>
45#include <stdlib.h>
46#include <str.h>
47#include <str_error.h>
48#include <ctype.h>
49#include <macros.h>
50#include <stdlib.h>
51#include <dirent.h>
52#include <ipc/irc.h>
53#include <ipc/services.h>
54#include <vfs/vfs.h>
55#include <irc.h>
56#include <ns.h>
57
58#include <ddf/driver.h>
59#include <ddf/log.h>
60#include <ops/hw_res.h>
61#include <ops/pio_window.h>
62
63#include <device/hw_res.h>
64#include <device/pio_window.h>
65
66#include <pci_dev_iface.h>
67
68#include "i8237.h"
69
70#define NAME "isa"
71#define ISA_CHILD_FUN_CONF_PATH "/drv/isa/isa.dev"
72#define EBUS_CHILD_FUN_CONF_PATH "/drv/isa/ebus.dev"
73
74#define ISA_MAX_HW_RES 6
75
76typedef struct {
77 fibril_mutex_t mutex;
78 bool pci_isa_bridge;
79 uint16_t pci_vendor_id;
80 uint16_t pci_device_id;
81 uint8_t pci_class;
82 uint8_t pci_subclass;
83 ddf_dev_t *dev;
84 ddf_fun_t *fctl;
85 pio_window_t pio_win;
86 list_t functions;
87} isa_bus_t;
88
89typedef struct isa_fun {
90 fibril_mutex_t mutex;
91 ddf_fun_t *fnode;
92 hw_resource_t resources[ISA_MAX_HW_RES];
93 hw_resource_list_t hw_resources;
94 link_t bus_link;
95 isa_bus_t *bus;
96} isa_fun_t;
97
98/** Obtain soft-state from device node */
99static isa_bus_t *isa_bus(ddf_dev_t *dev)
100{
101 return ddf_dev_data_get(dev);
102}
103
104/** Obtain soft-state from function node */
105static isa_fun_t *isa_fun(ddf_fun_t *fun)
106{
107 return ddf_fun_data_get(fun);
108}
109
110static hw_resource_list_t *isa_fun_get_resources(ddf_fun_t *fnode)
111{
112 isa_fun_t *fun = isa_fun(fnode);
113 assert(fun);
114
115 return &fun->hw_resources;
116}
117
118static bool isa_fun_owns_interrupt(isa_fun_t *fun, int irq)
119{
120 const hw_resource_list_t *res = &fun->hw_resources;
121
122 /* Check that specified irq really belongs to the function */
123 for (size_t i = 0; i < res->count; ++i) {
124 if (res->resources[i].type == INTERRUPT &&
125 res->resources[i].res.interrupt.irq == irq) {
126 return true;
127 }
128 }
129
130 return false;
131}
132
133static errno_t isa_fun_enable_interrupt(ddf_fun_t *fnode, int irq)
134{
135 isa_fun_t *fun = isa_fun(fnode);
136
137 if (!isa_fun_owns_interrupt(fun, irq))
138 return EINVAL;
139
140 return irc_enable_interrupt(irq);
141}
142
143static errno_t isa_fun_disable_interrupt(ddf_fun_t *fnode, int irq)
144{
145 isa_fun_t *fun = isa_fun(fnode);
146
147 if (!isa_fun_owns_interrupt(fun, irq))
148 return EINVAL;
149
150 return irc_disable_interrupt(irq);
151}
152
153static errno_t isa_fun_clear_interrupt(ddf_fun_t *fnode, int irq)
154{
155 isa_fun_t *fun = isa_fun(fnode);
156
157 if (!isa_fun_owns_interrupt(fun, irq))
158 return EINVAL;
159
160 return irc_clear_interrupt(irq);
161}
162
163static errno_t isa_fun_setup_dma(ddf_fun_t *fnode,
164 unsigned int channel, uint32_t pa, uint32_t size, uint8_t mode)
165{
166 assert(fnode);
167 isa_fun_t *fun = isa_fun(fnode);
168 assert(fun);
169 const hw_resource_list_t *res = &fun->hw_resources;
170 assert(res);
171
172 for (size_t i = 0; i < res->count; ++i) {
173 /* Check for assigned channel */
174 if (((res->resources[i].type == DMA_CHANNEL_16) &&
175 (res->resources[i].res.dma_channel.dma16 == channel)) ||
176 ((res->resources[i].type == DMA_CHANNEL_8) &&
177 (res->resources[i].res.dma_channel.dma8 == channel))) {
178 return dma_channel_setup(channel, pa, size, mode);
179 }
180 }
181
182 return EINVAL;
183}
184
185static errno_t isa_fun_remain_dma(ddf_fun_t *fnode,
186 unsigned channel, size_t *size)
187{
188 assert(size);
189 assert(fnode);
190 isa_fun_t *fun = isa_fun(fnode);
191 assert(fun);
192 const hw_resource_list_t *res = &fun->hw_resources;
193 assert(res);
194
195 for (size_t i = 0; i < res->count; ++i) {
196 /* Check for assigned channel */
197 if (((res->resources[i].type == DMA_CHANNEL_16) &&
198 (res->resources[i].res.dma_channel.dma16 == channel)) ||
199 ((res->resources[i].type == DMA_CHANNEL_8) &&
200 (res->resources[i].res.dma_channel.dma8 == channel))) {
201 return dma_channel_remain(channel, size);
202 }
203 }
204
205 return EINVAL;
206}
207
208/** Handle legacy IO availability query from function.
209 *
210 * @param fnode Function performing the query
211 * @param rclaims Place to store the legacy IO claims bitmask
212 * @return EOK on success or an error code
213 */
214static errno_t isa_fun_query_legacy_io(ddf_fun_t *fnode,
215 hw_res_claims_t *rclaims)
216{
217 isa_fun_t *fun = isa_fun(fnode);
218 hw_res_claims_t claims;
219 async_sess_t *sess;
220 errno_t rc;
221
222 ddf_msg(LVL_DEBUG, "isa_fun_query_legacy_io()");
223
224 sess = ddf_dev_parent_sess_get(fun->bus->dev);
225 if (sess == NULL) {
226 ddf_msg(LVL_ERROR, "isa_dev_add failed to connect to the "
227 "parent driver.");
228 return ENOENT;
229 }
230
231 if (!fun->bus->pci_isa_bridge) {
232 ddf_msg(LVL_NOTE, "isa_fun_query_legacy_io: classic ISA - "
233 "legacy IDE range is available");
234 /* Classic ISA, we can be sure IDE is unclaimed by PCI */
235 claims = 0;
236 } else {
237 ddf_msg(LVL_NOTE, "isa_fun_query_legacy_io: ISA bridge - "
238 "determine legacy IDE availability from PCI bus driver");
239 rc = hw_res_query_legacy_io(sess, &claims);
240 if (rc != EOK) {
241 ddf_msg(LVL_NOTE, "Error querying legacy IO claims.");
242 return rc;
243 }
244 }
245
246 ddf_msg(LVL_DEBUG, "isa_fun_query_legacy_io: returning 0x%x", claims);
247 *rclaims = claims;
248 return EOK;
249}
250
251static hw_res_ops_t isa_fun_hw_res_ops = {
252 .get_resource_list = isa_fun_get_resources,
253 .enable_interrupt = isa_fun_enable_interrupt,
254 .disable_interrupt = isa_fun_disable_interrupt,
255 .clear_interrupt = isa_fun_clear_interrupt,
256 .dma_channel_setup = isa_fun_setup_dma,
257 .dma_channel_remain = isa_fun_remain_dma,
258 .query_legacy_io = isa_fun_query_legacy_io
259};
260
261static pio_window_t *isa_fun_get_pio_window(ddf_fun_t *fnode)
262{
263 ddf_dev_t *dev = ddf_fun_get_dev(fnode);
264 isa_bus_t *isa = isa_bus(dev);
265 assert(isa);
266
267 return &isa->pio_win;
268}
269
270static pio_window_ops_t isa_fun_pio_window_ops = {
271 .get_pio_window = isa_fun_get_pio_window
272};
273
274static ddf_dev_ops_t isa_fun_ops = {
275 .interfaces[HW_RES_DEV_IFACE] = &isa_fun_hw_res_ops,
276 .interfaces[PIO_WINDOW_DEV_IFACE] = &isa_fun_pio_window_ops,
277};
278
279static errno_t isa_dev_add(ddf_dev_t *dev);
280static errno_t isa_dev_remove(ddf_dev_t *dev);
281static errno_t isa_fun_online(ddf_fun_t *fun);
282static errno_t isa_fun_offline(ddf_fun_t *fun);
283
284/** The isa device driver's standard operations */
285static driver_ops_t isa_ops = {
286 .dev_add = &isa_dev_add,
287 .dev_remove = &isa_dev_remove,
288 .fun_online = &isa_fun_online,
289 .fun_offline = &isa_fun_offline
290};
291
292/** The isa device driver structure. */
293static driver_t isa_driver = {
294 .name = NAME,
295 .driver_ops = &isa_ops
296};
297
298static isa_fun_t *isa_fun_create(isa_bus_t *isa, const char *name)
299{
300 ddf_fun_t *fnode = ddf_fun_create(isa->dev, fun_inner, name);
301 if (fnode == NULL)
302 return NULL;
303
304 isa_fun_t *fun = ddf_fun_data_alloc(fnode, sizeof(isa_fun_t));
305 if (fun == NULL) {
306 ddf_fun_destroy(fnode);
307 return NULL;
308 }
309
310 fibril_mutex_initialize(&fun->mutex);
311 fun->hw_resources.resources = fun->resources;
312 fun->bus = isa;
313
314 fun->fnode = fnode;
315 return fun;
316}
317
318static char *fun_conf_read(const char *conf_path)
319{
320 bool suc = false;
321 char *buf = NULL;
322 bool opened = false;
323 int fd;
324 size_t len;
325 errno_t rc;
326 size_t nread;
327 vfs_stat_t st;
328
329 rc = vfs_lookup_open(conf_path, WALK_REGULAR, MODE_READ, &fd);
330 if (rc != EOK) {
331 ddf_msg(LVL_ERROR, "Unable to open %s", conf_path);
332 goto cleanup;
333 }
334
335 opened = true;
336
337 if (vfs_stat(fd, &st) != EOK) {
338 ddf_msg(LVL_ERROR, "Unable to vfs_stat %d", fd);
339 goto cleanup;
340 }
341
342 len = st.size;
343 if (len == 0) {
344 ddf_msg(LVL_ERROR, "Configuration file '%s' is empty.",
345 conf_path);
346 goto cleanup;
347 }
348
349 buf = malloc(len + 1);
350 if (buf == NULL) {
351 ddf_msg(LVL_ERROR, "Memory allocation failed.");
352 goto cleanup;
353 }
354
355 rc = vfs_read(fd, (aoff64_t []) { 0 }, buf, len, &nread);
356 if (rc != EOK) {
357 ddf_msg(LVL_ERROR, "Unable to read file '%s'.", conf_path);
358 goto cleanup;
359 }
360
361 buf[nread] = 0;
362
363 suc = true;
364
365cleanup:
366 if (!suc && buf != NULL) {
367 free(buf);
368 buf = NULL;
369 }
370
371 if (opened)
372 vfs_put(fd);
373
374 return buf;
375}
376
377static char *str_get_line(char *str, char **next)
378{
379 char *line = str;
380 *next = NULL;
381
382 if (str == NULL) {
383 return NULL;
384 }
385
386 while (*str != '\0' && *str != '\n') {
387 str++;
388 }
389
390 if (*str != '\0') {
391 *next = str + 1;
392 }
393
394 *str = '\0';
395 return line;
396}
397
398static bool line_empty(const char *line)
399{
400 while (line != NULL && *line != 0) {
401 if (!isspace(*line))
402 return false;
403 line++;
404 }
405
406 return true;
407}
408
409static char *get_device_name(char *line)
410{
411 /* Skip leading spaces. */
412 while (*line != '\0' && isspace(*line)) {
413 line++;
414 }
415
416 /* Get the name part of the rest of the line. */
417 str_tok(line, ":", NULL);
418 return line;
419}
420
421static inline const char *skip_spaces(const char *line)
422{
423 /* Skip leading spaces. */
424 while (*line != '\0' && isspace(*line))
425 line++;
426
427 return line;
428}
429
430static void isa_fun_add_irq(isa_fun_t *fun, int irq)
431{
432 size_t count = fun->hw_resources.count;
433 hw_resource_t *resources = fun->hw_resources.resources;
434
435 if (count < ISA_MAX_HW_RES) {
436 resources[count].type = INTERRUPT;
437 resources[count].res.interrupt.irq = irq;
438
439 fun->hw_resources.count++;
440
441 ddf_msg(LVL_NOTE, "Added irq 0x%x to function %s", irq,
442 ddf_fun_get_name(fun->fnode));
443 }
444}
445
446static void isa_fun_add_dma(isa_fun_t *fun, int dma)
447{
448 size_t count = fun->hw_resources.count;
449 hw_resource_t *resources = fun->hw_resources.resources;
450
451 if (count < ISA_MAX_HW_RES) {
452 if ((dma > 0) && (dma < 4)) {
453 resources[count].type = DMA_CHANNEL_8;
454 resources[count].res.dma_channel.dma8 = dma;
455
456 fun->hw_resources.count++;
457 ddf_msg(LVL_NOTE, "Added dma 0x%x to function %s", dma,
458 ddf_fun_get_name(fun->fnode));
459
460 return;
461 }
462
463 if ((dma > 4) && (dma < 8)) {
464 resources[count].type = DMA_CHANNEL_16;
465 resources[count].res.dma_channel.dma16 = dma;
466
467 fun->hw_resources.count++;
468 ddf_msg(LVL_NOTE, "Added dma 0x%x to function %s", dma,
469 ddf_fun_get_name(fun->fnode));
470
471 return;
472 }
473
474 ddf_msg(LVL_WARN, "Skipped dma 0x%x for function %s", dma,
475 ddf_fun_get_name(fun->fnode));
476 }
477}
478
479static void isa_fun_add_io_range(isa_fun_t *fun, size_t addr, size_t len)
480{
481 size_t count = fun->hw_resources.count;
482 hw_resource_t *resources = fun->hw_resources.resources;
483
484 isa_bus_t *isa = isa_bus(ddf_fun_get_dev(fun->fnode));
485
486 if (count < ISA_MAX_HW_RES) {
487 resources[count].type = IO_RANGE;
488 resources[count].res.io_range.address = addr;
489 resources[count].res.io_range.address += isa->pio_win.io.base;
490 resources[count].res.io_range.size = len;
491 resources[count].res.io_range.relative = false;
492 resources[count].res.io_range.endianness = LITTLE_ENDIAN;
493
494 fun->hw_resources.count++;
495
496 ddf_msg(LVL_NOTE, "Added io range (addr=0x%x, size=0x%x) to "
497 "function %s", (unsigned int) addr, (unsigned int) len,
498 ddf_fun_get_name(fun->fnode));
499 }
500}
501
502static void isa_fun_add_mem_range(isa_fun_t *fun, uintptr_t addr, size_t len)
503{
504 size_t count = fun->hw_resources.count;
505 hw_resource_t *resources = fun->hw_resources.resources;
506
507 isa_bus_t *isa = isa_bus(ddf_fun_get_dev(fun->fnode));
508
509 if (count < ISA_MAX_HW_RES) {
510 resources[count].type = MEM_RANGE;
511 resources[count].res.mem_range.address = addr;
512 resources[count].res.mem_range.address += isa->pio_win.mem.base;
513 resources[count].res.mem_range.size = len;
514 resources[count].res.mem_range.relative = false;
515 resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
516
517 fun->hw_resources.count++;
518
519 ddf_msg(LVL_NOTE, "Added mem range (addr=0x%zx, size=0x%x) to "
520 "function %s", (uintptr_t) addr, (unsigned int) len,
521 ddf_fun_get_name(fun->fnode));
522 }
523}
524
525static void fun_parse_irq(isa_fun_t *fun, const char *val)
526{
527 int irq = 0;
528 char *end = NULL;
529
530 val = skip_spaces(val);
531 irq = (int) strtol(val, &end, 10);
532
533 if (val != end)
534 isa_fun_add_irq(fun, irq);
535}
536
537static void fun_parse_dma(isa_fun_t *fun, const char *val)
538{
539 char *end = NULL;
540
541 val = skip_spaces(val);
542 const int dma = strtol(val, &end, 10);
543
544 if (val != end)
545 isa_fun_add_dma(fun, dma);
546}
547
548static void fun_parse_io_range(isa_fun_t *fun, const char *val)
549{
550 size_t addr, len;
551 char *end = NULL;
552
553 val = skip_spaces(val);
554 addr = strtol(val, &end, 0x10);
555
556 if (val == end)
557 return;
558
559 val = skip_spaces(end);
560 len = strtol(val, &end, 0x10);
561
562 if (val == end)
563 return;
564
565 isa_fun_add_io_range(fun, addr, len);
566}
567
568static void fun_parse_mem_range(isa_fun_t *fun, const char *val)
569{
570 uintptr_t addr;
571 size_t len;
572 char *end = NULL;
573
574 val = skip_spaces(val);
575 addr = strtoul(val, &end, 0x10);
576
577 if (val == end)
578 return;
579
580 val = skip_spaces(end);
581 len = strtol(val, &end, 0x10);
582
583 if (val == end)
584 return;
585
586 isa_fun_add_mem_range(fun, addr, len);
587}
588
589static void get_match_id(char **id, const char *val)
590{
591 const char *end = val;
592
593 while (!isspace(*end))
594 end++;
595
596 size_t size = end - val + 1;
597 *id = (char *)malloc(size);
598 str_cpy(*id, size, val);
599}
600
601static void fun_parse_match_id(isa_fun_t *fun, const char *val)
602{
603 char *id = NULL;
604 char *end = NULL;
605
606 val = skip_spaces(val);
607
608 int score = (int)strtol(val, &end, 10);
609 if (val == end) {
610 ddf_msg(LVL_ERROR, "Cannot read match score for function "
611 "%s.", ddf_fun_get_name(fun->fnode));
612 return;
613 }
614
615 val = skip_spaces(end);
616 get_match_id(&id, val);
617 if (id == NULL) {
618 ddf_msg(LVL_ERROR, "Cannot read match ID for function %s.",
619 ddf_fun_get_name(fun->fnode));
620 return;
621 }
622
623 ddf_msg(LVL_DEBUG, "Adding match id '%s' with score %d to "
624 "function %s", id, score, ddf_fun_get_name(fun->fnode));
625
626 errno_t rc = ddf_fun_add_match_id(fun->fnode, id, score);
627 if (rc != EOK) {
628 ddf_msg(LVL_ERROR, "Failed adding match ID: %s",
629 str_error(rc));
630 }
631
632 free(id);
633}
634
635static bool prop_parse(isa_fun_t *fun, const char *line, const char *prop,
636 void (*read_fn)(isa_fun_t *, const char *))
637{
638 size_t proplen = str_size(prop);
639
640 if (str_lcmp(line, prop, proplen) == 0) {
641 line += proplen;
642 line = skip_spaces(line);
643 (*read_fn)(fun, line);
644
645 return true;
646 }
647
648 return false;
649}
650
651static void fun_prop_parse(isa_fun_t *fun, const char *line)
652{
653 /* Skip leading spaces. */
654 line = skip_spaces(line);
655
656 if (!prop_parse(fun, line, "io_range", &fun_parse_io_range) &&
657 !prop_parse(fun, line, "mem_range", &fun_parse_mem_range) &&
658 !prop_parse(fun, line, "irq", &fun_parse_irq) &&
659 !prop_parse(fun, line, "dma", &fun_parse_dma) &&
660 !prop_parse(fun, line, "match", &fun_parse_match_id)) {
661
662 ddf_msg(LVL_ERROR, "Undefined device property at line '%s'",
663 line);
664 }
665}
666
667static char *isa_fun_read_info(char *fun_conf, isa_bus_t *isa)
668{
669 char *line;
670
671 /* Skip empty lines. */
672 do {
673 line = str_get_line(fun_conf, &fun_conf);
674
675 if (line == NULL) {
676 /* no more lines */
677 return NULL;
678 }
679
680 } while (line_empty(line));
681
682 /* Get device name. */
683 const char *fun_name = get_device_name(line);
684 if (fun_name == NULL)
685 return NULL;
686
687 isa_fun_t *fun = isa_fun_create(isa, fun_name);
688 if (fun == NULL) {
689 return NULL;
690 }
691
692 /* Get properties of the device (match ids, irq and io range). */
693 while (true) {
694 line = str_get_line(fun_conf, &fun_conf);
695
696 if (line_empty(line)) {
697 /* no more device properties */
698 break;
699 }
700
701 /*
702 * Get the device's property from the configuration line
703 * and store it in the device structure.
704 */
705 fun_prop_parse(fun, line);
706 }
707
708 /* Set device operations to the device. */
709 ddf_fun_set_ops(fun->fnode, &isa_fun_ops);
710
711 ddf_msg(LVL_DEBUG, "Binding function %s.", ddf_fun_get_name(fun->fnode));
712
713 /* XXX Handle error */
714 (void) ddf_fun_bind(fun->fnode);
715
716 list_append(&fun->bus_link, &isa->functions);
717
718 return fun_conf;
719}
720
721static void isa_functions_add(isa_bus_t *isa)
722{
723#define BASE_CLASS_BRIDGE 0x06
724#define SUB_CLASS_BRIDGE_ISA 0x01
725 bool isa_bridge = ((isa->pci_class == BASE_CLASS_BRIDGE) &&
726 (isa->pci_subclass == SUB_CLASS_BRIDGE_ISA));
727
728#define VENDOR_ID_SUN 0x108e
729#define DEVICE_ID_EBUS 0x1000
730 bool ebus = ((isa->pci_vendor_id == VENDOR_ID_SUN) &&
731 (isa->pci_device_id == DEVICE_ID_EBUS));
732
733 const char *conf_path = NULL;
734 if (isa_bridge)
735 conf_path = ISA_CHILD_FUN_CONF_PATH;
736 else if (ebus)
737 conf_path = EBUS_CHILD_FUN_CONF_PATH;
738
739 char *conf = fun_conf_read(conf_path);
740 while (conf != NULL && *conf != '\0') {
741 conf = isa_fun_read_info(conf, isa);
742 }
743 free(conf);
744}
745
746static errno_t isa_read_pci_cfg(isa_bus_t *isa, async_sess_t *sess)
747{
748 errno_t rc;
749
750 rc = pci_config_space_read_16(sess, PCI_VENDOR_ID, &isa->pci_vendor_id);
751 if (rc != EOK)
752 return rc;
753 rc = pci_config_space_read_16(sess, PCI_DEVICE_ID, &isa->pci_device_id);
754 if (rc != EOK)
755 return rc;
756 rc = pci_config_space_read_8(sess, PCI_BASE_CLASS, &isa->pci_class);
757 if (rc != EOK)
758 return rc;
759 rc = pci_config_space_read_8(sess, PCI_SUB_CLASS, &isa->pci_subclass);
760 if (rc != EOK)
761 return rc;
762
763 return EOK;
764}
765
766static errno_t isa_dev_add(ddf_dev_t *dev)
767{
768 async_sess_t *sess;
769 errno_t rc;
770
771 ddf_msg(LVL_DEBUG, "isa_dev_add, device handle = %d",
772 (int) ddf_dev_get_handle(dev));
773
774 isa_bus_t *isa = ddf_dev_data_alloc(dev, sizeof(isa_bus_t));
775 if (isa == NULL)
776 return ENOMEM;
777
778 fibril_mutex_initialize(&isa->mutex);
779 isa->dev = dev;
780 list_initialize(&isa->functions);
781
782 sess = ddf_dev_parent_sess_get(dev);
783 if (sess == NULL) {
784 ddf_msg(LVL_ERROR, "isa_dev_add failed to connect to the "
785 "parent driver.");
786 return ENOENT;
787 }
788
789 rc = isa_read_pci_cfg(isa, sess);
790 if (rc != EOK) {
791 ddf_msg(LVL_NOTE, "Cannot read PCI config. Assuming ISA classic.");
792 isa->pci_isa_bridge = false;
793 isa->pci_vendor_id = 0;
794 isa->pci_device_id = 0;
795 isa->pci_class = BASE_CLASS_BRIDGE;
796 isa->pci_subclass = SUB_CLASS_BRIDGE_ISA;
797 } else {
798 ddf_msg(LVL_NOTE, "ISA Bridge");
799 isa->pci_isa_bridge = true;
800 }
801
802 rc = pio_window_get(sess, &isa->pio_win);
803 if (rc != EOK) {
804 ddf_msg(LVL_ERROR, "isa_dev_add failed to get PIO window "
805 "for the device.");
806 return rc;
807 }
808
809 /* Make the bus device more visible. Does not do anything. */
810 ddf_msg(LVL_DEBUG, "Adding a 'ctl' function");
811
812 fibril_mutex_lock(&isa->mutex);
813
814 isa->fctl = ddf_fun_create(dev, fun_exposed, "ctl");
815 if (isa->fctl == NULL) {
816 ddf_msg(LVL_ERROR, "Failed creating control function.");
817 return EXDEV;
818 }
819
820 if (ddf_fun_bind(isa->fctl) != EOK) {
821 ddf_fun_destroy(isa->fctl);
822 ddf_msg(LVL_ERROR, "Failed binding control function.");
823 return EXDEV;
824 }
825
826 /* Add functions as specified in the configuration file. */
827 isa_functions_add(isa);
828 ddf_msg(LVL_NOTE, "Finished enumerating legacy functions");
829
830 fibril_mutex_unlock(&isa->mutex);
831
832 return EOK;
833}
834
835static errno_t isa_dev_remove(ddf_dev_t *dev)
836{
837 isa_bus_t *isa = isa_bus(dev);
838
839 fibril_mutex_lock(&isa->mutex);
840
841 while (!list_empty(&isa->functions)) {
842 isa_fun_t *fun = list_get_instance(list_first(&isa->functions),
843 isa_fun_t, bus_link);
844
845 errno_t rc = ddf_fun_offline(fun->fnode);
846 if (rc != EOK) {
847 fibril_mutex_unlock(&isa->mutex);
848 ddf_msg(LVL_ERROR, "Failed offlining %s", ddf_fun_get_name(fun->fnode));
849 return rc;
850 }
851
852 rc = ddf_fun_unbind(fun->fnode);
853 if (rc != EOK) {
854 fibril_mutex_unlock(&isa->mutex);
855 ddf_msg(LVL_ERROR, "Failed unbinding %s", ddf_fun_get_name(fun->fnode));
856 return rc;
857 }
858
859 list_remove(&fun->bus_link);
860
861 ddf_fun_destroy(fun->fnode);
862 }
863
864 if (ddf_fun_unbind(isa->fctl) != EOK) {
865 fibril_mutex_unlock(&isa->mutex);
866 ddf_msg(LVL_ERROR, "Failed unbinding control function.");
867 return EXDEV;
868 }
869
870 fibril_mutex_unlock(&isa->mutex);
871
872 return EOK;
873}
874
875static errno_t isa_fun_online(ddf_fun_t *fun)
876{
877 ddf_msg(LVL_DEBUG, "isa_fun_online()");
878 return ddf_fun_online(fun);
879}
880
881static errno_t isa_fun_offline(ddf_fun_t *fun)
882{
883 ddf_msg(LVL_DEBUG, "isa_fun_offline()");
884 return ddf_fun_offline(fun);
885}
886
887int main(int argc, char *argv[])
888{
889 printf(NAME ": HelenOS ISA bus driver\n");
890 ddf_log_init(NAME);
891 return ddf_driver_main(&isa_driver);
892}
893
894/**
895 * @}
896 */
Note: See TracBrowser for help on using the repository browser.