source: mainline/uspace/drv/bus/isa/isa.c@ 08e103d4

Last change on this file since 08e103d4 was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

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