source: mainline/uspace/drv/bus/isa/isa.c@ 3be9d10

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3be9d10 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

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