--- trunk/src/device.c 2007/10/08 16:17:48 2 +++ trunk/src/device.c 2007/10/08 16:21:17 34 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Anders Gavare. All rights reserved. + * Copyright (C) 2005-2007 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: device.c,v 1.13 2005/02/26 17:37:25 debug Exp $ + * $Id: device.c,v 1.32 2006/12/30 13:30:51 debug Exp $ * * Device registry framework. */ @@ -35,6 +35,8 @@ #include #include "device.h" +#include "machine.h" +#include "memory.h" #include "misc.h" @@ -43,6 +45,9 @@ static int n_device_entries = 0; static int device_exit_on_error = 1; +static struct pci_entry *pci_entries = NULL; +static int n_pci_entries = 0; + /* * device_entry_compar(): @@ -105,6 +110,57 @@ /* + * pci_register(): + * + * Registers a pci device. The pci device is added to the pci_entries array. + * + * Return value is 1 if the pci device was registered. If it was not + * added, this function does not return. + */ +int pci_register(char *name, void (*initf)(struct machine *, struct memory *, + struct pci_device *)) +{ + pci_entries = realloc(pci_entries, sizeof(struct pci_entry) + * (n_pci_entries + 1)); + if (pci_entries == NULL) { + fprintf(stderr, "pci_register(): out of memory\n"); + exit(1); + } + + memset(&pci_entries[n_pci_entries], 0, sizeof(struct pci_entry)); + + pci_entries[n_pci_entries].name = strdup(name); + pci_entries[n_pci_entries].initf = initf; + n_pci_entries ++; + return 1; +} + + +/* + * pci_lookup_initf(): + * + * Find a pci device init function by scanning the pci_entries array. + * + * Return value is a function pointer, or NULL if the name was not found. + */ +void (*pci_lookup_initf(const char *name))(struct machine *machine, + struct memory *mem, struct pci_device *pd) +{ + int i; + + if (name == NULL) { + fprintf(stderr, "pci_lookup_initf(): name = NULL\n"); + exit(1); + } + + for (i=0; i= n_device_entries) - i = n_device_entries - 1; + while (lo <= hi) { + int r, i = (lo + hi) / 2; - /* printf("device_lookup(): i=%i step=%i\n", i, step); + /* printf("device_lookup(): i=%i (lo=%i hi=%i)\n", i, lo, hi); printf(" name='%s', '%s'\n", name, device_entries[i].name); */ r = strcmp(name, device_entries[i].name); - - if (r < 0) { - /* Go left: */ - i -= step; - if (step == 0) - i --; - } else if (r > 0) { - /* Go right: */ - i += step; - if (step == 0) - i ++; - } else { + if (r == 0) { /* Found it! */ return &device_entries[i]; } - if (do_return) - return NULL; - - if (step == 0) - do_return = 1; - - step /= 2; + /* Try left or right half: */ + if (r < 0) + hi = i - 1; + if (r > 0) + lo = i + 1; } + + return NULL; } @@ -178,7 +219,7 @@ */ int device_unregister(char *name) { - size_t i; + ssize_t i; struct device_entry *p = device_lookup(name); if (p == NULL) { @@ -213,22 +254,26 @@ /* * device_add(): * - * Add a device to a machine. + * Add a device to a machine. For example: "kn210 addr=0x12340000" adds a + * device called "kn210" at a specific address. * - * "kn210 addr=0x12340000" adds a kn210 device at a specific address. + * TODO: This function is quite ugly, and should be cleaned up. + * name_and_params should be const. */ void *device_add(struct machine *machine, char *name_and_params) { struct device_entry *p; struct devinit devinit; char *s2, *s3; - size_t len; + size_t len, interrupt_path_len = strlen(machine->path) + 100; + int quoted; memset(&devinit, 0, sizeof(struct devinit)); devinit.machine = machine; /* Default values: */ devinit.addr_mult = 1; + devinit.in_use = 1; /* Get the device name first: */ s2 = name_and_params; @@ -244,6 +289,15 @@ memcpy(devinit.name, name_and_params, len); devinit.name[len] = '\0'; + /* Allocate space for the default interrupt name: */ + devinit.interrupt_path = malloc(interrupt_path_len + 1); + if (devinit.interrupt_path == NULL) { + fprintf(stderr, "device_add(): out of memory\n"); + exit(1); + } + snprintf(devinit.interrupt_path, interrupt_path_len, + "%s.cpu[%i]", machine->path, machine->bootstrap_cpu); + p = device_lookup(devinit.name); if (p == NULL) { fatal("no such device (\"%s\")\n", devinit.name); @@ -280,12 +334,54 @@ if (strncmp(s2, "addr=", 5) == 0) { devinit.addr = mystrtoull(s3, NULL, 0); + } else if (strncmp(s2, "addr2=", 6) == 0) { + devinit.addr2 = mystrtoull(s3, NULL, 0); } else if (strncmp(s2, "len=", 4) == 0) { devinit.len = mystrtoull(s3, NULL, 0); } else if (strncmp(s2, "addr_mult=", 10) == 0) { devinit.addr_mult = mystrtoull(s3, NULL, 0); + } else if (strncmp(s2, "pci_little_endian=", 18) == 0) { + devinit.pci_little_endian = mystrtoull(s3, NULL, 0); + switch (devinit.pci_little_endian) { + case 0: break; + case 1: devinit.pci_little_endian = + MEM_PCI_LITTLE_ENDIAN; + break; + default:fatal("Bad pci_little_endian value.\n"); + exit(1); + } } else if (strncmp(s2, "irq=", 4) == 0) { devinit.irq_nr = mystrtoull(s3, NULL, 0); + + /* New-style interrupt path: */ + snprintf(devinit.interrupt_path, interrupt_path_len,s3); + if (strchr(devinit.interrupt_path, ' ') != NULL) + *strchr(devinit.interrupt_path, ' ') = '\0'; + + if (strncmp(s3, "none", 4) == 0) + devinit.interrupt_path[0] = '\0'; + } else if (strncmp(s2, "in_use=", 7) == 0) { + devinit.in_use = mystrtoull(s3, NULL, 0); + } else if (strncmp(s2, "name2=", 6) == 0) { + char *h = s2 + 6; + size_t len = 0; + quoted = 0; + while (*h) { + if (*h == '\'') + quoted = !quoted; + h++, len++; + if (!quoted && *h == ' ') + break; + } + devinit.name2 = malloc(len + 1); + if (devinit.name2 == NULL) { + fprintf(stderr, "out of memory\n"); + exit(1); + } + h = s2 + 6; + if (*h == '\'') + len -= 2, h++; + snprintf(devinit.name2, len + 1, h); } else { fatal("unknown param: %s\n", s2); if (device_exit_on_error) @@ -296,8 +392,13 @@ /* skip to the next param: */ s2 = s3; - while (*s2 != '\0' && *s2 != ' ' && *s2 != ',' && *s2 != ';') + quoted = 0; + while (*s2 != '\0' && (*s2 != ' ' || quoted) && + *s2 != ',' && *s2 != ';') { + if (*s2 == '\'') + quoted = !quoted; s2 ++; + } } @@ -315,6 +416,7 @@ goto return_fail; } + free(devinit.interrupt_path); free(devinit.name); return devinit.return_ptr; @@ -349,6 +451,11 @@ /* * device_set_exit_on_error(): + * + * This function selects the behaviour of the emulator when a device is not + * found. During startup, it is nicest to abort the whole emulator session, + * but if a device addition is attempted from within the debugger, then it is + * nicer to just print a warning and continue. */ void device_set_exit_on_error(int exit_on_error) { @@ -360,7 +467,7 @@ * device_init(): * * Initialize the device registry, and call autodev_init() to automatically - * add all normal devices (from the devices/ directory). + * add all normal devices (from the src/devices/ directory). * * This function should be called before any other device_*() function is used. */