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