isa.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010 Lenka Trochtova
00003  * Copyright (c) 2011 Jiri Svoboda
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * - Redistributions of source code must retain the above copyright
00011  *   notice, this list of conditions and the following disclaimer.
00012  * - Redistributions in binary form must reproduce the above copyright
00013  *   notice, this list of conditions and the following disclaimer in the
00014  *   documentation and/or other materials provided with the distribution.
00015  * - The name of the author may not be used to endorse or promote products
00016  *   derived from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00019  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00020  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00021  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00023  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00024  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00025  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00026  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00027  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00039 #include <assert.h>
00040 #include <stdio.h>
00041 #include <errno.h>
00042 #include <bool.h>
00043 #include <fibril_synch.h>
00044 #include <stdlib.h>
00045 #include <str.h>
00046 #include <str_error.h>
00047 #include <ctype.h>
00048 #include <macros.h>
00049 #include <malloc.h>
00050 #include <dirent.h>
00051 #include <fcntl.h>
00052 #include <sys/stat.h>
00053 
00054 #include <ddf/driver.h>
00055 #include <ddf/log.h>
00056 #include <ops/hw_res.h>
00057 
00058 #include <devman.h>
00059 #include <ipc/devman.h>
00060 #include <device/hw_res.h>
00061 
00062 #define NAME "isa"
00063 #define CHILD_FUN_CONF_PATH "/drv/isa/isa.dev"
00064 
00066 #define ISA_FUN(fnode) ((isa_fun_t *) ((fnode)->driver_data))
00067 
00068 #define ISA_MAX_HW_RES 4
00069 
00070 typedef struct isa_fun {
00071         ddf_fun_t *fnode;
00072         hw_resource_list_t hw_resources;
00073 } isa_fun_t;
00074 
00075 static hw_resource_list_t *isa_get_fun_resources(ddf_fun_t *fnode)
00076 {
00077         isa_fun_t *fun = ISA_FUN(fnode);
00078         assert(fun != NULL);
00079 
00080         return &fun->hw_resources;
00081 }
00082 
00083 static bool isa_enable_fun_interrupt(ddf_fun_t *fnode)
00084 {
00085         /* TODO */
00086 
00087         return false;
00088 }
00089 
00090 static hw_res_ops_t isa_fun_hw_res_ops = {
00091         &isa_get_fun_resources,
00092         &isa_enable_fun_interrupt
00093 };
00094 
00095 static ddf_dev_ops_t isa_fun_ops;
00096 
00097 static int isa_add_device(ddf_dev_t *dev);
00098 
00100 static driver_ops_t isa_ops = {
00101         .add_device = &isa_add_device
00102 };
00103 
00105 static driver_t isa_driver = {
00106         .name = NAME,
00107         .driver_ops = &isa_ops
00108 };
00109 
00110 static isa_fun_t *isa_fun_create(ddf_dev_t *dev, const char *name)
00111 {
00112         isa_fun_t *fun = calloc(1, sizeof(isa_fun_t));
00113         if (fun == NULL)
00114                 return NULL;
00115 
00116         ddf_fun_t *fnode = ddf_fun_create(dev, fun_inner, name);
00117         if (fnode == NULL) {
00118                 free(fun);
00119                 return NULL;
00120         }
00121 
00122         fun->fnode = fnode;
00123         fnode->driver_data = fun;
00124         return fun;
00125 }
00126 
00127 static char *fun_conf_read(const char *conf_path)
00128 {
00129         bool suc = false;
00130         char *buf = NULL;
00131         bool opened = false;
00132         int fd;
00133         size_t len = 0;
00134 
00135         fd = open(conf_path, O_RDONLY);
00136         if (fd < 0) {
00137                 ddf_msg(LVL_ERROR, "Unable to open %s", conf_path);
00138                 goto cleanup;
00139         }
00140 
00141         opened = true;
00142 
00143         len = lseek(fd, 0, SEEK_END);
00144         lseek(fd, 0, SEEK_SET);
00145         if (len == 0) {
00146                 ddf_msg(LVL_ERROR, "Configuration file '%s' is empty.",
00147                     conf_path);
00148                 goto cleanup;
00149         }
00150 
00151         buf = malloc(len + 1);
00152         if (buf == NULL) {
00153                 ddf_msg(LVL_ERROR, "Memory allocation failed.");
00154                 goto cleanup;
00155         }
00156 
00157         if (0 >= read(fd, buf, len)) {
00158                 ddf_msg(LVL_ERROR, "Unable to read file '%s'.", conf_path);
00159                 goto cleanup;
00160         }
00161 
00162         buf[len] = 0;
00163 
00164         suc = true;
00165 
00166 cleanup:
00167         if (!suc && buf != NULL) {
00168                 free(buf);
00169                 buf = NULL;
00170         }
00171 
00172         if (opened)
00173                 close(fd);
00174 
00175         return buf;
00176 }
00177 
00178 static char *str_get_line(char *str, char **next)
00179 {
00180         char *line = str;
00181 
00182         if (str == NULL) {
00183                 *next = NULL;
00184                 return NULL;
00185         }
00186 
00187         while (*str != '\0' && *str != '\n') {
00188                 str++;
00189         }
00190 
00191         if (*str != '\0') {
00192                 *next = str + 1;
00193         } else {
00194                 *next = NULL;
00195         }
00196 
00197         *str = '\0';
00198         return line;
00199 }
00200 
00201 static bool line_empty(const char *line)
00202 {
00203         while (line != NULL && *line != 0) {
00204                 if (!isspace(*line))
00205                         return false;
00206                 line++;
00207         }
00208 
00209         return true;
00210 }
00211 
00212 static char *get_device_name(char *line)
00213 {
00214         /* Skip leading spaces. */
00215         while (*line != '\0' && isspace(*line)) {
00216                 line++;
00217         }
00218 
00219         /* Get the name part of the rest of the line. */
00220         strtok(line, ":");
00221 
00222         /* Allocate output buffer. */
00223         size_t size = str_size(line) + 1;
00224         char *name = malloc(size);
00225 
00226         if (name != NULL) {
00227                 /* Copy the result to the output buffer. */
00228                 str_cpy(name, size, line);
00229         }
00230 
00231         return name;
00232 }
00233 
00234 static inline char *skip_spaces(char *line)
00235 {
00236         /* Skip leading spaces. */
00237         while (*line != '\0' && isspace(*line))
00238                 line++;
00239 
00240         return line;
00241 }
00242 
00243 static void isa_fun_set_irq(isa_fun_t *fun, int irq)
00244 {
00245         size_t count = fun->hw_resources.count;
00246         hw_resource_t *resources = fun->hw_resources.resources;
00247 
00248         if (count < ISA_MAX_HW_RES) {
00249                 resources[count].type = INTERRUPT;
00250                 resources[count].res.interrupt.irq = irq;
00251 
00252                 fun->hw_resources.count++;
00253 
00254                 ddf_msg(LVL_NOTE, "Added irq 0x%x to function %s", irq,
00255                     fun->fnode->name);
00256         }
00257 }
00258 
00259 static void isa_fun_set_io_range(isa_fun_t *fun, size_t addr, size_t len)
00260 {
00261         size_t count = fun->hw_resources.count;
00262         hw_resource_t *resources = fun->hw_resources.resources;
00263 
00264         if (count < ISA_MAX_HW_RES) {
00265                 resources[count].type = IO_RANGE;
00266                 resources[count].res.io_range.address = addr;
00267                 resources[count].res.io_range.size = len;
00268                 resources[count].res.io_range.endianness = LITTLE_ENDIAN;
00269 
00270                 fun->hw_resources.count++;
00271 
00272                 ddf_msg(LVL_NOTE, "Added io range (addr=0x%x, size=0x%x) to "
00273                     "function %s", (unsigned int) addr, (unsigned int) len,
00274                     fun->fnode->name);
00275         }
00276 }
00277 
00278 static void fun_parse_irq(isa_fun_t *fun, char *val)
00279 {
00280         int irq = 0;
00281         char *end = NULL;
00282 
00283         val = skip_spaces(val);
00284         irq = (int)strtol(val, &end, 0x10);
00285 
00286         if (val != end)
00287                 isa_fun_set_irq(fun, irq);
00288 }
00289 
00290 static void fun_parse_io_range(isa_fun_t *fun, char *val)
00291 {
00292         size_t addr, len;
00293         char *end = NULL;
00294 
00295         val = skip_spaces(val);
00296         addr = strtol(val, &end, 0x10);
00297 
00298         if (val == end)
00299                 return;
00300 
00301         val = skip_spaces(end);
00302         len = strtol(val, &end, 0x10);
00303 
00304         if (val == end)
00305                 return;
00306 
00307         isa_fun_set_io_range(fun, addr, len);
00308 }
00309 
00310 static void get_match_id(char **id, char *val)
00311 {
00312         char *end = val;
00313 
00314         while (!isspace(*end))
00315                 end++;
00316 
00317         size_t size = end - val + 1;
00318         *id = (char *)malloc(size);
00319         str_cpy(*id, size, val);
00320 }
00321 
00322 static void fun_parse_match_id(isa_fun_t *fun, char *val)
00323 {
00324         char *id = NULL;
00325         int score = 0;
00326         char *end = NULL;
00327         int rc;
00328 
00329         val = skip_spaces(val);
00330 
00331         score = (int)strtol(val, &end, 10);
00332         if (val == end) {
00333                 ddf_msg(LVL_ERROR, "Cannot read match score for function "
00334                     "%s.", fun->fnode->name);
00335                 return;
00336         }
00337 
00338         val = skip_spaces(end);
00339         get_match_id(&id, val);
00340         if (id == NULL) {
00341                 ddf_msg(LVL_ERROR, "Cannot read match ID for function %s.",
00342                     fun->fnode->name);
00343                 return;
00344         }
00345 
00346         ddf_msg(LVL_DEBUG, "Adding match id '%s' with score %d to "
00347             "function %s", id, score, fun->fnode->name);
00348 
00349         rc = ddf_fun_add_match_id(fun->fnode, id, score);
00350         if (rc != EOK) {
00351                 ddf_msg(LVL_ERROR, "Failed adding match ID: %s",
00352                     str_error(rc));
00353         }
00354 }
00355 
00356 static bool prop_parse(isa_fun_t *fun, char *line, const char *prop,
00357     void (*read_fn)(isa_fun_t *, char *))
00358 {
00359         size_t proplen = str_size(prop);
00360 
00361         if (str_lcmp(line, prop, proplen) == 0) {
00362                 line += proplen;
00363                 line = skip_spaces(line);
00364                 (*read_fn)(fun, line);
00365 
00366                 return true;
00367         }
00368 
00369         return false;
00370 }
00371 
00372 static void fun_prop_parse(isa_fun_t *fun, char *line)
00373 {
00374         /* Skip leading spaces. */
00375         line = skip_spaces(line);
00376 
00377         if (!prop_parse(fun, line, "io_range", &fun_parse_io_range) &&
00378             !prop_parse(fun, line, "irq", &fun_parse_irq) &&
00379             !prop_parse(fun, line, "match", &fun_parse_match_id)) {
00380 
00381                 ddf_msg(LVL_ERROR, "Undefined device property at line '%s'",
00382                     line);
00383         }
00384 }
00385 
00386 static void fun_hw_res_alloc(isa_fun_t *fun)
00387 {
00388         fun->hw_resources.resources = 
00389             (hw_resource_t *)malloc(sizeof(hw_resource_t) * ISA_MAX_HW_RES);
00390 }
00391 
00392 static char *isa_fun_read_info(char *fun_conf, ddf_dev_t *dev)
00393 {
00394         char *line;
00395         char *fun_name = NULL;
00396 
00397         /* Skip empty lines. */
00398         while (true) {
00399                 line = str_get_line(fun_conf, &fun_conf);
00400 
00401                 if (line == NULL) {
00402                         /* no more lines */
00403                         return NULL;
00404                 }
00405 
00406                 if (!line_empty(line))
00407                         break;
00408         }
00409 
00410         /* Get device name. */
00411         fun_name = get_device_name(line);
00412         if (fun_name == NULL)
00413                 return NULL;
00414 
00415         isa_fun_t *fun = isa_fun_create(dev, fun_name);
00416         if (fun == NULL) {
00417                 free(fun_name);
00418                 return NULL;
00419         }
00420 
00421         /* Allocate buffer for the list of hardware resources of the device. */
00422         fun_hw_res_alloc(fun);
00423 
00424         /* Get properties of the device (match ids, irq and io range). */
00425         while (true) {
00426                 line = str_get_line(fun_conf, &fun_conf);
00427 
00428                 if (line_empty(line)) {
00429                         /* no more device properties */
00430                         break;
00431                 }
00432 
00433                 /*
00434                  * Get the device's property from the configuration line
00435                  * and store it in the device structure.
00436                  */
00437                 fun_prop_parse(fun, line);
00438         }
00439 
00440         /* Set device operations to the device. */
00441         fun->fnode->ops = &isa_fun_ops;
00442 
00443         ddf_msg(LVL_DEBUG, "Binding function %s.", fun->fnode->name);
00444 
00445         /* XXX Handle error */
00446         (void) ddf_fun_bind(fun->fnode);
00447 
00448         return fun_conf;
00449 }
00450 
00451 static void fun_conf_parse(char *conf, ddf_dev_t *dev)
00452 {
00453         while (conf != NULL && *conf != '\0') {
00454                 conf = isa_fun_read_info(conf, dev);
00455         }
00456 }
00457 
00458 static void isa_functions_add(ddf_dev_t *dev)
00459 {
00460         char *fun_conf;
00461 
00462         fun_conf = fun_conf_read(CHILD_FUN_CONF_PATH);
00463         if (fun_conf != NULL) {
00464                 fun_conf_parse(fun_conf, dev);
00465                 free(fun_conf);
00466         }
00467 }
00468 
00469 static int isa_add_device(ddf_dev_t *dev)
00470 {
00471         ddf_msg(LVL_DEBUG, "isa_add_device, device handle = %d",
00472             (int) dev->handle);
00473 
00474         /* Make the bus device more visible. Does not do anything. */
00475         ddf_msg(LVL_DEBUG, "Adding a 'ctl' function");
00476 
00477         ddf_fun_t *ctl = ddf_fun_create(dev, fun_exposed, "ctl");
00478         if (ctl == NULL) {
00479                 ddf_msg(LVL_ERROR, "Failed creating control function.");
00480                 return EXDEV;
00481         }
00482 
00483         if (ddf_fun_bind(ctl) != EOK) {
00484                 ddf_msg(LVL_ERROR, "Failed binding control function.");
00485                 return EXDEV;
00486         }
00487 
00488         /* Add functions as specified in the configuration file. */
00489         isa_functions_add(dev);
00490         ddf_msg(LVL_NOTE, "Finished enumerating legacy functions");
00491 
00492         return EOK;
00493 }
00494 
00495 static void isa_init() 
00496 {
00497         ddf_log_init(NAME, LVL_ERROR);
00498         isa_fun_ops.interfaces[HW_RES_DEV_IFACE] = &isa_fun_hw_res_ops;
00499 }
00500 
00501 int main(int argc, char *argv[])
00502 {
00503         printf(NAME ": HelenOS ISA bus driver\n");
00504         isa_init();
00505         return ddf_driver_main(&isa_driver);
00506 }
00507 

Generated on Thu Jun 2 07:45:44 2011 for HelenOS/USB by  doxygen 1.4.7