source: mainline/uspace/drv/bus/isa/isa.c@ 0dab4850

Last change on this file since 0dab4850 was 443695e, checked in by Jiri Svoboda <jiri@…>, 17 months ago

Basic PCI-IDE driver (no DMA support)

Also, make sure we avoid attaching ISA IDE and PCI IDE
at the same time. For simplicity, use ISA IDE on ISA systems
and PCI IDE on PCI-based systems.

  • Property mode set to 100644
File size: 19.6 KB
Line 
1/*
2 * Copyright (c) 2024 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
208static errno_t isa_fun_get_flags(ddf_fun_t *fnode, hw_res_flags_t *rflags)
209{
210 isa_fun_t *fun = isa_fun(fnode);
211 hw_res_flags_t flags;
212
213 flags = 0;
214 if (fun->bus->pci_isa_bridge)
215 flags |= hwf_isa_bridge;
216
217 ddf_msg(LVL_NOTE, "isa_fun_get_flags: returning 0x%x", flags);
218 *rflags = flags;
219 return EOK;
220}
221
222static hw_res_ops_t isa_fun_hw_res_ops = {
223 .get_resource_list = isa_fun_get_resources,
224 .enable_interrupt = isa_fun_enable_interrupt,
225 .disable_interrupt = isa_fun_disable_interrupt,
226 .clear_interrupt = isa_fun_clear_interrupt,
227 .dma_channel_setup = isa_fun_setup_dma,
228 .dma_channel_remain = isa_fun_remain_dma,
229 .get_flags = isa_fun_get_flags
230};
231
232static pio_window_t *isa_fun_get_pio_window(ddf_fun_t *fnode)
233{
234 ddf_dev_t *dev = ddf_fun_get_dev(fnode);
235 isa_bus_t *isa = isa_bus(dev);
236 assert(isa);
237
238 return &isa->pio_win;
239}
240
241static pio_window_ops_t isa_fun_pio_window_ops = {
242 .get_pio_window = isa_fun_get_pio_window
243};
244
245static ddf_dev_ops_t isa_fun_ops = {
246 .interfaces[HW_RES_DEV_IFACE] = &isa_fun_hw_res_ops,
247 .interfaces[PIO_WINDOW_DEV_IFACE] = &isa_fun_pio_window_ops,
248};
249
250static errno_t isa_dev_add(ddf_dev_t *dev);
251static errno_t isa_dev_remove(ddf_dev_t *dev);
252static errno_t isa_fun_online(ddf_fun_t *fun);
253static errno_t isa_fun_offline(ddf_fun_t *fun);
254
255/** The isa device driver's standard operations */
256static driver_ops_t isa_ops = {
257 .dev_add = &isa_dev_add,
258 .dev_remove = &isa_dev_remove,
259 .fun_online = &isa_fun_online,
260 .fun_offline = &isa_fun_offline
261};
262
263/** The isa device driver structure. */
264static driver_t isa_driver = {
265 .name = NAME,
266 .driver_ops = &isa_ops
267};
268
269static isa_fun_t *isa_fun_create(isa_bus_t *isa, const char *name)
270{
271 ddf_fun_t *fnode = ddf_fun_create(isa->dev, fun_inner, name);
272 if (fnode == NULL)
273 return NULL;
274
275 isa_fun_t *fun = ddf_fun_data_alloc(fnode, sizeof(isa_fun_t));
276 if (fun == NULL) {
277 ddf_fun_destroy(fnode);
278 return NULL;
279 }
280
281 fibril_mutex_initialize(&fun->mutex);
282 fun->hw_resources.resources = fun->resources;
283 fun->bus = isa;
284
285 fun->fnode = fnode;
286 return fun;
287}
288
289static char *fun_conf_read(const char *conf_path)
290{
291 bool suc = false;
292 char *buf = NULL;
293 bool opened = false;
294 int fd;
295 size_t len;
296 errno_t rc;
297 size_t nread;
298 vfs_stat_t st;
299
300 rc = vfs_lookup_open(conf_path, WALK_REGULAR, MODE_READ, &fd);
301 if (rc != EOK) {
302 ddf_msg(LVL_ERROR, "Unable to open %s", conf_path);
303 goto cleanup;
304 }
305
306 opened = true;
307
308 if (vfs_stat(fd, &st) != EOK) {
309 ddf_msg(LVL_ERROR, "Unable to vfs_stat %d", fd);
310 goto cleanup;
311 }
312
313 len = st.size;
314 if (len == 0) {
315 ddf_msg(LVL_ERROR, "Configuration file '%s' is empty.",
316 conf_path);
317 goto cleanup;
318 }
319
320 buf = malloc(len + 1);
321 if (buf == NULL) {
322 ddf_msg(LVL_ERROR, "Memory allocation failed.");
323 goto cleanup;
324 }
325
326 rc = vfs_read(fd, (aoff64_t []) { 0 }, buf, len, &nread);
327 if (rc != EOK) {
328 ddf_msg(LVL_ERROR, "Unable to read file '%s'.", conf_path);
329 goto cleanup;
330 }
331
332 buf[nread] = 0;
333
334 suc = true;
335
336cleanup:
337 if (!suc && buf != NULL) {
338 free(buf);
339 buf = NULL;
340 }
341
342 if (opened)
343 vfs_put(fd);
344
345 return buf;
346}
347
348static char *str_get_line(char *str, char **next)
349{
350 char *line = str;
351 *next = NULL;
352
353 if (str == NULL) {
354 return NULL;
355 }
356
357 while (*str != '\0' && *str != '\n') {
358 str++;
359 }
360
361 if (*str != '\0') {
362 *next = str + 1;
363 }
364
365 *str = '\0';
366 return line;
367}
368
369static bool line_empty(const char *line)
370{
371 while (line != NULL && *line != 0) {
372 if (!isspace(*line))
373 return false;
374 line++;
375 }
376
377 return true;
378}
379
380static char *get_device_name(char *line)
381{
382 /* Skip leading spaces. */
383 while (*line != '\0' && isspace(*line)) {
384 line++;
385 }
386
387 /* Get the name part of the rest of the line. */
388 str_tok(line, ":", NULL);
389 return line;
390}
391
392static inline const char *skip_spaces(const char *line)
393{
394 /* Skip leading spaces. */
395 while (*line != '\0' && isspace(*line))
396 line++;
397
398 return line;
399}
400
401static void isa_fun_add_irq(isa_fun_t *fun, int irq)
402{
403 size_t count = fun->hw_resources.count;
404 hw_resource_t *resources = fun->hw_resources.resources;
405
406 if (count < ISA_MAX_HW_RES) {
407 resources[count].type = INTERRUPT;
408 resources[count].res.interrupt.irq = irq;
409
410 fun->hw_resources.count++;
411
412 ddf_msg(LVL_NOTE, "Added irq 0x%x to function %s", irq,
413 ddf_fun_get_name(fun->fnode));
414 }
415}
416
417static void isa_fun_add_dma(isa_fun_t *fun, int dma)
418{
419 size_t count = fun->hw_resources.count;
420 hw_resource_t *resources = fun->hw_resources.resources;
421
422 if (count < ISA_MAX_HW_RES) {
423 if ((dma > 0) && (dma < 4)) {
424 resources[count].type = DMA_CHANNEL_8;
425 resources[count].res.dma_channel.dma8 = dma;
426
427 fun->hw_resources.count++;
428 ddf_msg(LVL_NOTE, "Added dma 0x%x to function %s", dma,
429 ddf_fun_get_name(fun->fnode));
430
431 return;
432 }
433
434 if ((dma > 4) && (dma < 8)) {
435 resources[count].type = DMA_CHANNEL_16;
436 resources[count].res.dma_channel.dma16 = dma;
437
438 fun->hw_resources.count++;
439 ddf_msg(LVL_NOTE, "Added dma 0x%x to function %s", dma,
440 ddf_fun_get_name(fun->fnode));
441
442 return;
443 }
444
445 ddf_msg(LVL_WARN, "Skipped dma 0x%x for function %s", dma,
446 ddf_fun_get_name(fun->fnode));
447 }
448}
449
450static void isa_fun_add_io_range(isa_fun_t *fun, size_t addr, size_t len)
451{
452 size_t count = fun->hw_resources.count;
453 hw_resource_t *resources = fun->hw_resources.resources;
454
455 isa_bus_t *isa = isa_bus(ddf_fun_get_dev(fun->fnode));
456
457 if (count < ISA_MAX_HW_RES) {
458 resources[count].type = IO_RANGE;
459 resources[count].res.io_range.address = addr;
460 resources[count].res.io_range.address += isa->pio_win.io.base;
461 resources[count].res.io_range.size = len;
462 resources[count].res.io_range.relative = false;
463 resources[count].res.io_range.endianness = LITTLE_ENDIAN;
464
465 fun->hw_resources.count++;
466
467 ddf_msg(LVL_NOTE, "Added io range (addr=0x%x, size=0x%x) to "
468 "function %s", (unsigned int) addr, (unsigned int) len,
469 ddf_fun_get_name(fun->fnode));
470 }
471}
472
473static void isa_fun_add_mem_range(isa_fun_t *fun, uintptr_t addr, size_t len)
474{
475 size_t count = fun->hw_resources.count;
476 hw_resource_t *resources = fun->hw_resources.resources;
477
478 isa_bus_t *isa = isa_bus(ddf_fun_get_dev(fun->fnode));
479
480 if (count < ISA_MAX_HW_RES) {
481 resources[count].type = MEM_RANGE;
482 resources[count].res.mem_range.address = addr;
483 resources[count].res.mem_range.address += isa->pio_win.mem.base;
484 resources[count].res.mem_range.size = len;
485 resources[count].res.mem_range.relative = false;
486 resources[count].res.mem_range.endianness = LITTLE_ENDIAN;
487
488 fun->hw_resources.count++;
489
490 ddf_msg(LVL_NOTE, "Added mem range (addr=0x%zx, size=0x%x) to "
491 "function %s", (uintptr_t) addr, (unsigned int) len,
492 ddf_fun_get_name(fun->fnode));
493 }
494}
495
496static void fun_parse_irq(isa_fun_t *fun, const char *val)
497{
498 int irq = 0;
499 char *end = NULL;
500
501 val = skip_spaces(val);
502 irq = (int) strtol(val, &end, 10);
503
504 if (val != end)
505 isa_fun_add_irq(fun, irq);
506}
507
508static void fun_parse_dma(isa_fun_t *fun, const char *val)
509{
510 char *end = NULL;
511
512 val = skip_spaces(val);
513 const int dma = strtol(val, &end, 10);
514
515 if (val != end)
516 isa_fun_add_dma(fun, dma);
517}
518
519static void fun_parse_io_range(isa_fun_t *fun, const char *val)
520{
521 size_t addr, len;
522 char *end = NULL;
523
524 val = skip_spaces(val);
525 addr = strtol(val, &end, 0x10);
526
527 if (val == end)
528 return;
529
530 val = skip_spaces(end);
531 len = strtol(val, &end, 0x10);
532
533 if (val == end)
534 return;
535
536 isa_fun_add_io_range(fun, addr, len);
537}
538
539static void fun_parse_mem_range(isa_fun_t *fun, const char *val)
540{
541 uintptr_t addr;
542 size_t len;
543 char *end = NULL;
544
545 val = skip_spaces(val);
546 addr = strtoul(val, &end, 0x10);
547
548 if (val == end)
549 return;
550
551 val = skip_spaces(end);
552 len = strtol(val, &end, 0x10);
553
554 if (val == end)
555 return;
556
557 isa_fun_add_mem_range(fun, addr, len);
558}
559
560static void get_match_id(char **id, const char *val)
561{
562 const char *end = val;
563
564 while (!isspace(*end))
565 end++;
566
567 size_t size = end - val + 1;
568 *id = (char *)malloc(size);
569 str_cpy(*id, size, val);
570}
571
572static void fun_parse_match_id(isa_fun_t *fun, const char *val)
573{
574 char *id = NULL;
575 char *end = NULL;
576
577 val = skip_spaces(val);
578
579 int score = (int)strtol(val, &end, 10);
580 if (val == end) {
581 ddf_msg(LVL_ERROR, "Cannot read match score for function "
582 "%s.", ddf_fun_get_name(fun->fnode));
583 return;
584 }
585
586 val = skip_spaces(end);
587 get_match_id(&id, val);
588 if (id == NULL) {
589 ddf_msg(LVL_ERROR, "Cannot read match ID for function %s.",
590 ddf_fun_get_name(fun->fnode));
591 return;
592 }
593
594 ddf_msg(LVL_DEBUG, "Adding match id '%s' with score %d to "
595 "function %s", id, score, ddf_fun_get_name(fun->fnode));
596
597 errno_t rc = ddf_fun_add_match_id(fun->fnode, id, score);
598 if (rc != EOK) {
599 ddf_msg(LVL_ERROR, "Failed adding match ID: %s",
600 str_error(rc));
601 }
602
603 free(id);
604}
605
606static bool prop_parse(isa_fun_t *fun, const char *line, const char *prop,
607 void (*read_fn)(isa_fun_t *, const char *))
608{
609 size_t proplen = str_size(prop);
610
611 if (str_lcmp(line, prop, proplen) == 0) {
612 line += proplen;
613 line = skip_spaces(line);
614 (*read_fn)(fun, line);
615
616 return true;
617 }
618
619 return false;
620}
621
622static void fun_prop_parse(isa_fun_t *fun, const char *line)
623{
624 /* Skip leading spaces. */
625 line = skip_spaces(line);
626
627 if (!prop_parse(fun, line, "io_range", &fun_parse_io_range) &&
628 !prop_parse(fun, line, "mem_range", &fun_parse_mem_range) &&
629 !prop_parse(fun, line, "irq", &fun_parse_irq) &&
630 !prop_parse(fun, line, "dma", &fun_parse_dma) &&
631 !prop_parse(fun, line, "match", &fun_parse_match_id)) {
632
633 ddf_msg(LVL_ERROR, "Undefined device property at line '%s'",
634 line);
635 }
636}
637
638static char *isa_fun_read_info(char *fun_conf, isa_bus_t *isa)
639{
640 char *line;
641
642 /* Skip empty lines. */
643 do {
644 line = str_get_line(fun_conf, &fun_conf);
645
646 if (line == NULL) {
647 /* no more lines */
648 return NULL;
649 }
650
651 } while (line_empty(line));
652
653 /* Get device name. */
654 const char *fun_name = get_device_name(line);
655 if (fun_name == NULL)
656 return NULL;
657
658 isa_fun_t *fun = isa_fun_create(isa, fun_name);
659 if (fun == NULL) {
660 return NULL;
661 }
662
663 /* Get properties of the device (match ids, irq and io range). */
664 while (true) {
665 line = str_get_line(fun_conf, &fun_conf);
666
667 if (line_empty(line)) {
668 /* no more device properties */
669 break;
670 }
671
672 /*
673 * Get the device's property from the configuration line
674 * and store it in the device structure.
675 */
676 fun_prop_parse(fun, line);
677 }
678
679 /* Set device operations to the device. */
680 ddf_fun_set_ops(fun->fnode, &isa_fun_ops);
681
682 ddf_msg(LVL_DEBUG, "Binding function %s.", ddf_fun_get_name(fun->fnode));
683
684 /* XXX Handle error */
685 (void) ddf_fun_bind(fun->fnode);
686
687 list_append(&fun->bus_link, &isa->functions);
688
689 return fun_conf;
690}
691
692static void isa_functions_add(isa_bus_t *isa)
693{
694#define BASE_CLASS_BRIDGE 0x06
695#define SUB_CLASS_BRIDGE_ISA 0x01
696 bool isa_bridge = ((isa->pci_class == BASE_CLASS_BRIDGE) &&
697 (isa->pci_subclass == SUB_CLASS_BRIDGE_ISA));
698
699#define VENDOR_ID_SUN 0x108e
700#define DEVICE_ID_EBUS 0x1000
701 bool ebus = ((isa->pci_vendor_id == VENDOR_ID_SUN) &&
702 (isa->pci_device_id == DEVICE_ID_EBUS));
703
704 const char *conf_path = NULL;
705 if (isa_bridge)
706 conf_path = ISA_CHILD_FUN_CONF_PATH;
707 else if (ebus)
708 conf_path = EBUS_CHILD_FUN_CONF_PATH;
709
710 char *conf = fun_conf_read(conf_path);
711 while (conf != NULL && *conf != '\0') {
712 conf = isa_fun_read_info(conf, isa);
713 }
714 free(conf);
715}
716
717static errno_t isa_read_pci_cfg(isa_bus_t *isa, async_sess_t *sess)
718{
719 errno_t rc;
720
721 rc = pci_config_space_read_16(sess, PCI_VENDOR_ID, &isa->pci_vendor_id);
722 if (rc != EOK)
723 return rc;
724 rc = pci_config_space_read_16(sess, PCI_DEVICE_ID, &isa->pci_device_id);
725 if (rc != EOK)
726 return rc;
727 rc = pci_config_space_read_8(sess, PCI_BASE_CLASS, &isa->pci_class);
728 if (rc != EOK)
729 return rc;
730 rc = pci_config_space_read_8(sess, PCI_SUB_CLASS, &isa->pci_subclass);
731 if (rc != EOK)
732 return rc;
733
734 return EOK;
735}
736
737static errno_t isa_dev_add(ddf_dev_t *dev)
738{
739 async_sess_t *sess;
740 errno_t rc;
741
742 ddf_msg(LVL_DEBUG, "isa_dev_add, device handle = %d",
743 (int) ddf_dev_get_handle(dev));
744
745 isa_bus_t *isa = ddf_dev_data_alloc(dev, sizeof(isa_bus_t));
746 if (isa == NULL)
747 return ENOMEM;
748
749 fibril_mutex_initialize(&isa->mutex);
750 isa->dev = dev;
751 list_initialize(&isa->functions);
752
753 sess = ddf_dev_parent_sess_get(dev);
754 if (sess == NULL) {
755 ddf_msg(LVL_ERROR, "isa_dev_add failed to connect to the "
756 "parent driver.");
757 return ENOENT;
758 }
759
760 rc = isa_read_pci_cfg(isa, sess);
761 if (rc != EOK) {
762 ddf_msg(LVL_NOTE, "Cannot read PCI config. Assuming ISA classic.");
763 isa->pci_isa_bridge = false;
764 isa->pci_vendor_id = 0;
765 isa->pci_device_id = 0;
766 isa->pci_class = BASE_CLASS_BRIDGE;
767 isa->pci_subclass = SUB_CLASS_BRIDGE_ISA;
768 } else {
769 ddf_msg(LVL_NOTE, "ISA Bridge");
770 isa->pci_isa_bridge = true;
771 }
772
773 rc = pio_window_get(sess, &isa->pio_win);
774 if (rc != EOK) {
775 ddf_msg(LVL_ERROR, "isa_dev_add failed to get PIO window "
776 "for the device.");
777 return rc;
778 }
779
780 /* Make the bus device more visible. Does not do anything. */
781 ddf_msg(LVL_DEBUG, "Adding a 'ctl' function");
782
783 fibril_mutex_lock(&isa->mutex);
784
785 isa->fctl = ddf_fun_create(dev, fun_exposed, "ctl");
786 if (isa->fctl == NULL) {
787 ddf_msg(LVL_ERROR, "Failed creating control function.");
788 return EXDEV;
789 }
790
791 if (ddf_fun_bind(isa->fctl) != EOK) {
792 ddf_fun_destroy(isa->fctl);
793 ddf_msg(LVL_ERROR, "Failed binding control function.");
794 return EXDEV;
795 }
796
797 /* Add functions as specified in the configuration file. */
798 isa_functions_add(isa);
799 ddf_msg(LVL_NOTE, "Finished enumerating legacy functions");
800
801 fibril_mutex_unlock(&isa->mutex);
802
803 return EOK;
804}
805
806static errno_t isa_dev_remove(ddf_dev_t *dev)
807{
808 isa_bus_t *isa = isa_bus(dev);
809
810 fibril_mutex_lock(&isa->mutex);
811
812 while (!list_empty(&isa->functions)) {
813 isa_fun_t *fun = list_get_instance(list_first(&isa->functions),
814 isa_fun_t, bus_link);
815
816 errno_t rc = ddf_fun_offline(fun->fnode);
817 if (rc != EOK) {
818 fibril_mutex_unlock(&isa->mutex);
819 ddf_msg(LVL_ERROR, "Failed offlining %s", ddf_fun_get_name(fun->fnode));
820 return rc;
821 }
822
823 rc = ddf_fun_unbind(fun->fnode);
824 if (rc != EOK) {
825 fibril_mutex_unlock(&isa->mutex);
826 ddf_msg(LVL_ERROR, "Failed unbinding %s", ddf_fun_get_name(fun->fnode));
827 return rc;
828 }
829
830 list_remove(&fun->bus_link);
831
832 ddf_fun_destroy(fun->fnode);
833 }
834
835 if (ddf_fun_unbind(isa->fctl) != EOK) {
836 fibril_mutex_unlock(&isa->mutex);
837 ddf_msg(LVL_ERROR, "Failed unbinding control function.");
838 return EXDEV;
839 }
840
841 fibril_mutex_unlock(&isa->mutex);
842
843 return EOK;
844}
845
846static errno_t isa_fun_online(ddf_fun_t *fun)
847{
848 ddf_msg(LVL_DEBUG, "isa_fun_online()");
849 return ddf_fun_online(fun);
850}
851
852static errno_t isa_fun_offline(ddf_fun_t *fun)
853{
854 ddf_msg(LVL_DEBUG, "isa_fun_offline()");
855 return ddf_fun_offline(fun);
856}
857
858int main(int argc, char *argv[])
859{
860 printf(NAME ": HelenOS ISA bus driver\n");
861 ddf_log_init(NAME);
862 return ddf_driver_main(&isa_driver);
863}
864
865/**
866 * @}
867 */
Note: See TracBrowser for help on using the repository browser.