source: mainline/uspace/drv/isa/isa.c@ 28a3e74

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 28a3e74 was 28a3e74, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Fix comment style.

  • Property mode set to 100644
File size: 10.6 KB
RevLine 
[892e4e1]1/*
2 * Copyright (c) 2010 Lenka Trochtova
[68414f4a]3 * Copyright (c) 2011 Jiri Svoboda
[892e4e1]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 * @defgroup isa ISA bus driver.
32 * @brief HelenOS ISA bus driver.
33 * @{
34 */
35
36/** @file
37 */
38
39#include <assert.h>
40#include <stdio.h>
41#include <errno.h>
42#include <bool.h>
43#include <fibril_synch.h>
44#include <stdlib.h>
[c47e1a8]45#include <str.h>
[cd0684d]46#include <str_error.h>
[892e4e1]47#include <ctype.h>
48#include <macros.h>
[c1a8ae52]49#include <malloc.h>
50#include <dirent.h>
51#include <fcntl.h>
52#include <sys/stat.h>
[892e4e1]53
[af6b5157]54#include <ddf/driver.h>
[fc51296]55#include <ddf/log.h>
[41b56084]56#include <ops/hw_res.h>
[5dc9622]57
[892e4e1]58#include <devman.h>
59#include <ipc/devman.h>
[5dc9622]60#include <device/hw_res.h>
[892e4e1]61
62#define NAME "isa"
[8b1e15ac]63#define CHILD_FUN_CONF_PATH "/drv/isa/isa.dev"
[892e4e1]64
[68414f4a]65/** Obtain soft-state pointer from function node pointer */
66#define ISA_FUN(fnode) ((isa_fun_t *) ((fnode)->driver_data))
67
[5dc9622]68#define ISA_MAX_HW_RES 4
69
[68414f4a]70typedef struct isa_fun {
[83a2f43]71 ddf_fun_t *fnode;
[032e0bb]72 hw_resource_list_t hw_resources;
[68414f4a]73} isa_fun_t;
[5dc9622]74
[83a2f43]75static hw_resource_list_t *isa_get_fun_resources(ddf_fun_t *fnode)
[5dc9622]76{
[68414f4a]77 isa_fun_t *fun = ISA_FUN(fnode);
78 assert(fun != NULL);
[032e0bb]79
[68414f4a]80 return &fun->hw_resources;
[5dc9622]81}
82
[83a2f43]83static bool isa_enable_fun_interrupt(ddf_fun_t *fnode)
[5dc9622]84{
[28a3e74]85 /* TODO */
[032e0bb]86
[5dc9622]87 return false;
88}
89
[8b1e15ac]90static hw_res_ops_t isa_fun_hw_res_ops = {
91 &isa_get_fun_resources,
92 &isa_enable_fun_interrupt
[5dc9622]93};
94
[83a2f43]95static ddf_dev_ops_t isa_fun_ops;
[5dc9622]96
[83a2f43]97static int isa_add_device(ddf_dev_t *dev);
[892e4e1]98
[032e0bb]99/** The isa device driver's standard operations */
[892e4e1]100static driver_ops_t isa_ops = {
101 .add_device = &isa_add_device
102};
103
[032e0bb]104/** The isa device driver structure. */
[892e4e1]105static driver_t isa_driver = {
106 .name = NAME,
107 .driver_ops = &isa_ops
108};
109
[83a2f43]110static isa_fun_t *isa_fun_create(ddf_dev_t *dev, const char *name)
[c1a8ae52]111{
[68414f4a]112 isa_fun_t *fun = calloc(1, sizeof(isa_fun_t));
[8b1e15ac]113 if (fun == NULL)
[c1a8ae52]114 return NULL;
[032e0bb]115
[83a2f43]116 ddf_fun_t *fnode = ddf_fun_create(dev, fun_inner, name);
[68414f4a]117 if (fnode == NULL) {
118 free(fun);
[c1a8ae52]119 return NULL;
120 }
[032e0bb]121
[68414f4a]122 fun->fnode = fnode;
123 fnode->driver_data = fun;
[8b1e15ac]124 return fun;
[c1a8ae52]125}
126
[68414f4a]127static char *fun_conf_read(const char *conf_path)
[032e0bb]128{
129 bool suc = false;
[c1a8ae52]130 char *buf = NULL;
131 bool opened = false;
[032e0bb]132 int fd;
[c47e1a8]133 size_t len = 0;
[032e0bb]134
[c1a8ae52]135 fd = open(conf_path, O_RDONLY);
136 if (fd < 0) {
[ebcb05a]137 ddf_msg(LVL_ERROR, "Unable to open %s", conf_path);
[c1a8ae52]138 goto cleanup;
[032e0bb]139 }
140
141 opened = true;
142
[c1a8ae52]143 len = lseek(fd, 0, SEEK_END);
[fc51296]144 lseek(fd, 0, SEEK_SET);
[c1a8ae52]145 if (len == 0) {
[ebcb05a]146 ddf_msg(LVL_ERROR, "Configuration file '%s' is empty.",
[fc51296]147 conf_path);
[032e0bb]148 goto cleanup;
[c1a8ae52]149 }
[032e0bb]150
[c1a8ae52]151 buf = malloc(len + 1);
152 if (buf == NULL) {
[ebcb05a]153 ddf_msg(LVL_ERROR, "Memory allocation failed.");
[c1a8ae52]154 goto cleanup;
[032e0bb]155 }
156
[c1a8ae52]157 if (0 >= read(fd, buf, len)) {
[ebcb05a]158 ddf_msg(LVL_ERROR, "Unable to read file '%s'.", conf_path);
[c1a8ae52]159 goto cleanup;
160 }
[032e0bb]161
[c1a8ae52]162 buf[len] = 0;
[032e0bb]163
[c1a8ae52]164 suc = true;
[032e0bb]165
[c1a8ae52]166cleanup:
[032e0bb]167 if (!suc && buf != NULL) {
168 free(buf);
[c1a8ae52]169 buf = NULL;
170 }
[032e0bb]171
172 if (opened)
173 close(fd);
174
175 return buf;
[c1a8ae52]176}
177
[032e0bb]178static char *str_get_line(char *str, char **next)
179{
[5dc9622]180 char *line = str;
[032e0bb]181
182 if (str == NULL) {
[f928a8a]183 *next = NULL;
184 return NULL;
185 }
[032e0bb]186
187 while (*str != '\0' && *str != '\n') {
[5dc9622]188 str++;
[032e0bb]189 }
190
191 if (*str != '\0') {
[5dc9622]192 *next = str + 1;
193 } else {
194 *next = NULL;
[032e0bb]195 }
196
197 *str = '\0';
[5dc9622]198 return line;
[c1a8ae52]199}
200
201static bool line_empty(const char *line)
202{
[032e0bb]203 while (line != NULL && *line != 0) {
204 if (!isspace(*line))
[c1a8ae52]205 return false;
[032e0bb]206 line++;
207 }
208
209 return true;
[c1a8ae52]210}
211
[032e0bb]212static char *get_device_name(char *line)
213{
214 /* Skip leading spaces. */
215 while (*line != '\0' && isspace(*line)) {
[c1a8ae52]216 line++;
217 }
[032e0bb]218
219 /* Get the name part of the rest of the line. */
220 strtok(line, ":");
221
222 /* Allocate output buffer. */
[5dc9622]223 size_t size = str_size(line) + 1;
224 char *name = malloc(size);
[032e0bb]225
226 if (name != NULL) {
227 /* Copy the result to the output buffer. */
[5dc9622]228 str_cpy(name, size, line);
[c1a8ae52]229 }
230
231 return name;
232}
233
[032e0bb]234static inline char *skip_spaces(char *line)
[c1a8ae52]235{
[032e0bb]236 /* Skip leading spaces. */
237 while (*line != '\0' && isspace(*line))
[c1a8ae52]238 line++;
239
[032e0bb]240 return line;
241}
[c1a8ae52]242
[68414f4a]243static void isa_fun_set_irq(isa_fun_t *fun, int irq)
[c1a8ae52]244{
[68414f4a]245 size_t count = fun->hw_resources.count;
246 hw_resource_t *resources = fun->hw_resources.resources;
[032e0bb]247
[5dc9622]248 if (count < ISA_MAX_HW_RES) {
249 resources[count].type = INTERRUPT;
250 resources[count].res.interrupt.irq = irq;
[032e0bb]251
[68414f4a]252 fun->hw_resources.count++;
[032e0bb]253
[ebcb05a]254 ddf_msg(LVL_NOTE, "Added irq 0x%x to function %s", irq,
[68414f4a]255 fun->fnode->name);
[032e0bb]256 }
[5dc9622]257}
258
[68414f4a]259static void isa_fun_set_io_range(isa_fun_t *fun, size_t addr, size_t len)
[5dc9622]260{
[68414f4a]261 size_t count = fun->hw_resources.count;
262 hw_resource_t *resources = fun->hw_resources.resources;
[032e0bb]263
[5dc9622]264 if (count < ISA_MAX_HW_RES) {
265 resources[count].type = IO_RANGE;
266 resources[count].res.io_range.address = addr;
267 resources[count].res.io_range.size = len;
[032e0bb]268 resources[count].res.io_range.endianness = LITTLE_ENDIAN;
269
[68414f4a]270 fun->hw_resources.count++;
[032e0bb]271
[fc51296]272 ddf_msg(LVL_NOTE, "Added io range (addr=0x%x, size=0x%x) to "
[ebcb05a]273 "function %s", (unsigned int) addr, (unsigned int) len,
[68414f4a]274 fun->fnode->name);
[032e0bb]275 }
[c1a8ae52]276}
277
[68414f4a]278static void fun_parse_irq(isa_fun_t *fun, char *val)
[c1a8ae52]279{
280 int irq = 0;
281 char *end = NULL;
[032e0bb]282
[8b1e15ac]283 val = skip_spaces(val);
[5dc9622]284 irq = (int)strtol(val, &end, 0x10);
[032e0bb]285
286 if (val != end)
[8b1e15ac]287 isa_fun_set_irq(fun, irq);
[c1a8ae52]288}
289
[68414f4a]290static void fun_parse_io_range(isa_fun_t *fun, char *val)
[c1a8ae52]291{
[5dc9622]292 size_t addr, len;
293 char *end = NULL;
[032e0bb]294
[8b1e15ac]295 val = skip_spaces(val);
[5dc9622]296 addr = strtol(val, &end, 0x10);
[032e0bb]297
298 if (val == end)
[5dc9622]299 return;
[032e0bb]300
[8b1e15ac]301 val = skip_spaces(end);
[5dc9622]302 len = strtol(val, &end, 0x10);
[032e0bb]303
304 if (val == end)
[5dc9622]305 return;
[032e0bb]306
[8b1e15ac]307 isa_fun_set_io_range(fun, addr, len);
[c1a8ae52]308}
309
[5dc9622]310static void get_match_id(char **id, char *val)
[c1a8ae52]311{
[5dc9622]312 char *end = val;
[032e0bb]313
314 while (!isspace(*end))
[5dc9622]315 end++;
[032e0bb]316
[f928a8a]317 size_t size = end - val + 1;
[5dc9622]318 *id = (char *)malloc(size);
[032e0bb]319 str_cpy(*id, size, val);
[5dc9622]320}
321
[68414f4a]322static void fun_parse_match_id(isa_fun_t *fun, char *val)
[032e0bb]323{
[5dc9622]324 char *id = NULL;
325 int score = 0;
326 char *end = NULL;
[cd0684d]327 int rc;
[032e0bb]328
[68414f4a]329 val = skip_spaces(val);
[032e0bb]330
[5fe1c32]331 score = (int)strtol(val, &end, 10);
[5dc9622]332 if (val == end) {
[fc51296]333 ddf_msg(LVL_ERROR, "Cannot read match score for function "
[ebcb05a]334 "%s.", fun->fnode->name);
[5dc9622]335 return;
336 }
[032e0bb]337
[bab6388]338 val = skip_spaces(end);
[5dc9622]339 get_match_id(&id, val);
[032e0bb]340 if (id == NULL) {
[ebcb05a]341 ddf_msg(LVL_ERROR, "Cannot read match ID for function %s.",
[fc51296]342 fun->fnode->name);
[5dc9622]343 return;
344 }
[032e0bb]345
[fc51296]346 ddf_msg(LVL_DEBUG, "Adding match id '%s' with score %d to "
[ebcb05a]347 "function %s", id, score, fun->fnode->name);
[cd0684d]348
349 rc = ddf_fun_add_match_id(fun->fnode, id, score);
[fc51296]350 if (rc != EOK) {
[ebcb05a]351 ddf_msg(LVL_ERROR, "Failed adding match ID: %s",
[fc51296]352 str_error(rc));
353 }
[c1a8ae52]354}
355
[68414f4a]356static bool prop_parse(isa_fun_t *fun, char *line, const char *prop,
357 void (*read_fn)(isa_fun_t *, char *))
[5fe1c32]358{
359 size_t proplen = str_size(prop);
[032e0bb]360
361 if (str_lcmp(line, prop, proplen) == 0) {
[5fe1c32]362 line += proplen;
363 line = skip_spaces(line);
[8b1e15ac]364 (*read_fn)(fun, line);
[032e0bb]365
[5fe1c32]366 return true;
367 }
[032e0bb]368
369 return false;
[5fe1c32]370}
371
[68414f4a]372static void fun_prop_parse(isa_fun_t *fun, char *line)
[c1a8ae52]373{
[032e0bb]374 /* Skip leading spaces. */
[c1a8ae52]375 line = skip_spaces(line);
[032e0bb]376
[68414f4a]377 if (!prop_parse(fun, line, "io_range", &fun_parse_io_range) &&
378 !prop_parse(fun, line, "irq", &fun_parse_irq) &&
[fc51296]379 !prop_parse(fun, line, "match", &fun_parse_match_id)) {
380
[ebcb05a]381 ddf_msg(LVL_ERROR, "Undefined device property at line '%s'",
[fc51296]382 line);
[032e0bb]383 }
[c1a8ae52]384}
385
[68414f4a]386static void fun_hw_res_alloc(isa_fun_t *fun)
[5dc9622]387{
[68414f4a]388 fun->hw_resources.resources =
[032e0bb]389 (hw_resource_t *)malloc(sizeof(hw_resource_t) * ISA_MAX_HW_RES);
[5dc9622]390}
391
[83a2f43]392static char *isa_fun_read_info(char *fun_conf, ddf_dev_t *dev)
[c1a8ae52]393{
394 char *line;
[8b1e15ac]395 char *fun_name = NULL;
[032e0bb]396
397 /* Skip empty lines. */
398 while (true) {
[8b1e15ac]399 line = str_get_line(fun_conf, &fun_conf);
[032e0bb]400
401 if (line == NULL) {
402 /* no more lines */
[c1a8ae52]403 return NULL;
404 }
[032e0bb]405
406 if (!line_empty(line))
[c1a8ae52]407 break;
408 }
[032e0bb]409
410 /* Get device name. */
[8b1e15ac]411 fun_name = get_device_name(line);
412 if (fun_name == NULL)
[c1a8ae52]413 return NULL;
[032e0bb]414
[97a62fe]415 isa_fun_t *fun = isa_fun_create(dev, fun_name);
[8b1e15ac]416 if (fun == NULL) {
417 free(fun_name);
[c1a8ae52]418 return NULL;
419 }
[032e0bb]420
421 /* Allocate buffer for the list of hardware resources of the device. */
[68414f4a]422 fun_hw_res_alloc(fun);
[032e0bb]423
424 /* Get properties of the device (match ids, irq and io range). */
425 while (true) {
[8b1e15ac]426 line = str_get_line(fun_conf, &fun_conf);
[032e0bb]427
[5dc9622]428 if (line_empty(line)) {
[032e0bb]429 /* no more device properties */
[5dc9622]430 break;
431 }
[032e0bb]432
433 /*
434 * Get the device's property from the configuration line
435 * and store it in the device structure.
436 */
[68414f4a]437 fun_prop_parse(fun, line);
[c1a8ae52]438 }
[032e0bb]439
440 /* Set device operations to the device. */
[97a62fe]441 fun->fnode->ops = &isa_fun_ops;
442
[ebcb05a]443 ddf_msg(LVL_DEBUG, "Binding function %s.", fun->fnode->name);
[032e0bb]444
[97a62fe]445 /* XXX Handle error */
446 (void) ddf_fun_bind(fun->fnode);
[032e0bb]447
[8b1e15ac]448 return fun_conf;
[c1a8ae52]449}
450
[83a2f43]451static void fun_conf_parse(char *conf, ddf_dev_t *dev)
[c1a8ae52]452{
[032e0bb]453 while (conf != NULL && *conf != '\0') {
[68414f4a]454 conf = isa_fun_read_info(conf, dev);
[032e0bb]455 }
[c1a8ae52]456}
457
[83a2f43]458static void isa_functions_add(ddf_dev_t *dev)
[c1a8ae52]459{
[8b1e15ac]460 char *fun_conf;
[032e0bb]461
[68414f4a]462 fun_conf = fun_conf_read(CHILD_FUN_CONF_PATH);
[8b1e15ac]463 if (fun_conf != NULL) {
[68414f4a]464 fun_conf_parse(fun_conf, dev);
[8b1e15ac]465 free(fun_conf);
[c1a8ae52]466 }
467}
[892e4e1]468
[83a2f43]469static int isa_add_device(ddf_dev_t *dev)
[892e4e1]470{
[ebcb05a]471 ddf_msg(LVL_DEBUG, "isa_add_device, device handle = %d",
[ab3a851]472 (int) dev->handle);
[032e0bb]473
[8b1e15ac]474 /* Make the bus device more visible. Does not do anything. */
[ebcb05a]475 ddf_msg(LVL_DEBUG, "Adding a 'ctl' function");
[8b1e15ac]476
[83a2f43]477 ddf_fun_t *ctl = ddf_fun_create(dev, fun_exposed, "ctl");
[97a62fe]478 if (ctl == NULL) {
[ebcb05a]479 ddf_msg(LVL_ERROR, "Failed creating control function.");
[97a62fe]480 return EXDEV;
481 }
482
483 if (ddf_fun_bind(ctl) != EOK) {
[ebcb05a]484 ddf_msg(LVL_ERROR, "Failed binding control function.");
[97a62fe]485 return EXDEV;
486 }
[8b1e15ac]487
[68414f4a]488 /* Add functions as specified in the configuration file. */
489 isa_functions_add(dev);
[ebcb05a]490 ddf_msg(LVL_NOTE, "Finished enumerating legacy functions");
[032e0bb]491
[df747b9c]492 return EOK;
[892e4e1]493}
494
[5dc9622]495static void isa_init()
496{
[fc51296]497 ddf_log_init(NAME, LVL_ERROR);
[68414f4a]498 isa_fun_ops.interfaces[HW_RES_DEV_IFACE] = &isa_fun_hw_res_ops;
[5dc9622]499}
500
[892e4e1]501int main(int argc, char *argv[])
502{
[032e0bb]503 printf(NAME ": HelenOS ISA bus driver\n");
[5dc9622]504 isa_init();
[83a2f43]505 return ddf_driver_main(&isa_driver);
[892e4e1]506}
507
508/**
509 * @}
510 */
Note: See TracBrowser for help on using the repository browser.