source: mainline/uspace/drv/bus/isa/isa.c@ 5b68e0c

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

ns8250 device removal support.

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