diff options
Diffstat (limited to 'src/bios_device.c')
-rw-r--r-- | src/bios_device.c | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/src/bios_device.c b/src/bios_device.c new file mode 100644 index 0000000..5c275c8 --- /dev/null +++ b/src/bios_device.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <net/if.h> +#include "list.h" +#include "bios_device.h" +#include "state.h" +#include "libbiosdevname.h" +#include "dmidecode/dmidecode.h" + +void free_bios_devices(void *cookie) +{ + struct libbiosdevname_state *state = cookie; + struct bios_device *dev, *n; + if (!state) + return; + list_for_each_entry_safe(dev, n, &state->bios_devices, node) { + list_del(&(dev->node)); + free(dev); + } +} + + +static void unparse_bios_device(struct bios_device *dev) +{ + char buf[200]; + printf("BIOS device: %s\n", dev->bios_name); + if (dev->netdev) { + unparse_network_device(buf, sizeof(buf), dev->netdev); + printf("%s", buf); + } + else + printf(" No driver loaded for this device.\n"); + + if (is_pci(dev)) { + unparse_pci_device(buf, sizeof(buf), dev->pcidev); + printf("%s", buf); + } + else if (is_pcmcia(dev)) { + unparse_pcmcia_device(buf, sizeof(buf), dev->pcmciadev); + printf("%s", buf); + } + + printf("\n"); +} + +void unparse_bios_devices(void *cookie) +{ + struct libbiosdevname_state *state = cookie; + struct bios_device *dev; + if (!state) + return; + list_for_each_entry(dev, &state->bios_devices, node) { + unparse_bios_device(dev); + } +} + +void unparse_bios_device_by_name(void *cookie, + const char *name) +{ + + struct libbiosdevname_state *state = cookie; + struct bios_device *dev; + if (!state) + return; + list_for_each_entry(dev, &state->bios_devices, node) { + if (dev->netdev && !strcmp(dev->netdev->kernel_name, name)) + unparse_bios_device(dev); + } +} + +char * kern_to_bios(void *cookie, + const char *name) +{ + struct libbiosdevname_state *state = cookie; + struct bios_device *dev; + if (!state) + return NULL; + list_for_each_entry(dev, &state->bios_devices, node) { + if (dev->netdev && !strcmp(dev->netdev->kernel_name, name)) + return dev->bios_name; + } + return NULL; +} + +void unparse_bios_device_list(void *cookie) +{ + struct libbiosdevname_state *state = cookie; + struct bios_device *dev; + if (!state) + return; + list_for_each_entry(dev, &state->bios_devices, node) { + unparse_bios_device(dev); + } +} + + + +/* + * This sorts all the embedded devices first; by PCI bus/dev/fn, then + * all the add-in devices by slot number, by pci bus/dev/fn. + * Unknown location devices show up as physical slot INT_MAX, so they + * come last. + */ + +static int sort_pci(const struct bios_device *bdev_a, const struct bios_device *bdev_b) +{ + const struct pci_device *a = bdev_a->pcidev; + const struct pci_device *b = bdev_b->pcidev; + + if (a->physical_slot < b->physical_slot) return -1; + else if (a->physical_slot > b->physical_slot) return 1; + + if (a->pci_dev.domain < b->pci_dev.domain) return -1; + else if (a->pci_dev.domain > b->pci_dev.domain) return 1; + + if (a->pci_dev.bus < b->pci_dev.bus) return -1; + else if (a->pci_dev.bus > b->pci_dev.bus) return 1; + + if (a->pci_dev.dev < b->pci_dev.dev) return -1; + else if (a->pci_dev.dev > b->pci_dev.dev) return 1; + + if (a->pci_dev.func < b->pci_dev.func) return -1; + else if (a->pci_dev.func > b->pci_dev.func) return 1; + + return 0; +} + +static int sort_smbios(const struct bios_device *x, const struct bios_device *y) +{ + struct pci_device *a, *b; + + if (x->pcidev && !y->pcidev) return -1; + else if (!x->pcidev && y->pcidev) return 1; + + a = x->pcidev; + b = y->pcidev; + + if (a->physical_slot == 0 && b->physical_slot == 0) { + if ( a->smbios_type == b->smbios_type) { + if ( a->smbios_instance < b->smbios_instance) return -1; + else if (a->smbios_instance > b->smbios_instance) return 1; + } + } + else { + if (a->physical_slot < b->physical_slot) return -1; + else if (a->physical_slot > b->physical_slot) return 1; + } + + return sort_pci(x, y); +} + + +static int sort_pcmcia(const struct bios_device *bdev_a, const struct bios_device *bdev_b) +{ + const struct pcmcia_device *a = bdev_a->pcmciadev; + const struct pcmcia_device *b = bdev_b->pcmciadev; + + if (a->socket < b->socket) return -1; + else if (a->socket > b->socket) return 1; + + if (a->function < b->function) return -1; + else if (a->function > b->function) return 1; + + return 0; +} + +enum bios_device_types { + IS_PCI, + IS_PCMCIA, + IS_UNKNOWN_TYPE, +}; + +static int bios_device_type_num(const struct bios_device *dev) +{ + if (is_pci(dev)) + return IS_PCI; + else if (is_pcmcia(dev)) + return IS_PCMCIA; + return IS_UNKNOWN_TYPE; +} + +static int sort_by_type(const struct bios_device *a, const struct bios_device *b) +{ + if (bios_device_type_num(a) < bios_device_type_num(b)) + return -1; + else if (bios_device_type_num(a) == bios_device_type_num(b)) { + if (is_pci(a)) + return sort_smbios(a, b); + else if (is_pcmcia(a)) + return sort_pcmcia(a, b); + else return 0; + } + else if (bios_device_type_num(a) > bios_device_type_num(b)) + return 1; + return 0; +} + + +static void insertion_sort_devices(struct bios_device *a, struct list_head *list, + int (*cmp)(const struct bios_device *, const struct bios_device *)) +{ + struct bios_device *b; + list_for_each_entry(b, list, node) { + if (cmp(a, b) <= 0) { + list_move_tail(&a->node, &b->node); + return; + } + } + list_move_tail(&a->node, list); +} + +static int set_slot_index(struct libbiosdevname_state *state) +{ + struct bios_device *dev; + int prevslot=-1; + int index=0; + + list_for_each_entry(dev, &state->bios_devices, node) { + if (!dev->pcidev) + continue; + if (dev->pcidev->physical_slot != prevslot) + index=0; + else + index++; + dev->pcidev->index_in_slot = index; + prevslot = dev->pcidev->physical_slot; + } + return 0; +} + +static void sort_device_list(struct libbiosdevname_state *state) +{ + LIST_HEAD(sorted_devices); + struct bios_device *dev, *tmp; + list_for_each_entry_safe(dev, tmp, &state->bios_devices, node) { + insertion_sort_devices(dev, &sorted_devices, sort_by_type); + } + list_splice(&sorted_devices, &state->bios_devices); + set_slot_index(state); +} + +static void match_eth_and_pci_devs(struct libbiosdevname_state *state) +{ + struct pci_device *p; + struct bios_device *b; + struct network_device *n; + char pci_name[40]; + + list_for_each_entry(p, &state->pci_devices, node) { + if (!is_pci_network(p)) + continue; + + unparse_pci_name(pci_name, sizeof(pci_name), &p->pci_dev); + n = find_net_device_by_bus_info(state, pci_name); + if (!n) + continue; + + b = malloc(sizeof(*b)); + if (!b) + continue; + memset(b, 0, sizeof(*b)); + INIT_LIST_HEAD(&b->node); + b->pcidev = p; + b->netdev = n; + claim_netdev(b->netdev); + list_add(&b->node, &state->bios_devices); + } +} + +static void match_eth_and_pcmcia(struct libbiosdevname_state *state) +{ + struct pcmcia_device *p; + struct bios_device *b; + char pcmcia_name[40]; + + list_for_each_entry(p, &state->pcmcia_devices, node) { + if (!is_pcmcia_network(p)) + continue; + + b = malloc(sizeof(*b)); + if (!b) + continue; + memset(b, 0, sizeof(*b)); + INIT_LIST_HEAD(&b->node); + b->pcmciadev = p; + + unparse_pcmcia_name(pcmcia_name, sizeof(pcmcia_name), p); + b->netdev = find_net_device_by_bus_info(state, pcmcia_name); + + memset(b->bios_name, 0, sizeof(b->bios_name)); + claim_netdev(b->netdev); + list_add(&b->node, &state->bios_devices); + } +} + +static void match_unknown_eths(struct libbiosdevname_state *state) +{ + struct bios_device *b; + struct network_device *n; + list_for_each_entry(n, &state->network_devices, node) + { + if (netdev_is_claimed(n)) + continue; + if (!drvinfo_valid(n)) + continue; + if (!is_ethernet(n)) /* for virtual interfaces */ + continue; + b = malloc(sizeof(*b)); + if (!b) + continue; + memset(b, 0, sizeof(*b)); + INIT_LIST_HEAD(&b->node); + b->netdev = n; + list_add(&b->node, &state->bios_devices); + } +} + + +static void match_all(struct libbiosdevname_state *state) +{ + match_eth_and_pci_devs(state); + match_eth_and_pcmcia(state); + match_unknown_eths(state); +} + +static struct libbiosdevname_state * alloc_state(void) +{ + struct libbiosdevname_state *state; + state = malloc(sizeof(*state)); + if (!state) + return NULL; + INIT_LIST_HEAD(&state->bios_devices); + INIT_LIST_HEAD(&state->pci_devices); + INIT_LIST_HEAD(&state->network_devices); + INIT_LIST_HEAD(&state->pcmcia_devices); + return state; +} + +void cleanup_bios_devices(void *cookie) +{ + struct libbiosdevname_state *state = cookie; + if (!state) + return; + free_bios_devices(state); + free_eths(state); + free_pcmcia_devices(state); + free_pci_devices(state); +} + +void * setup_bios_devices(int sortroutine, int namingpolicy) +{ + int rc=1; + struct libbiosdevname_state *state = alloc_state(); + + if (!state) + return NULL; + + rc = get_pci_devices(state); + if (rc) + goto out; + + rc = get_pcmcia_devices(state); + if (rc) + goto out; + rc = dmidecode_main(state); + if (rc) + goto out; + get_eths(state); + match_all(state); + if (sortroutine != nosort) { + sort_device_list(state); + } + assign_bios_network_names(state, sortroutine, namingpolicy); + return state; + +out: + cleanup_bios_devices(state); + free(state); + return NULL; +} + |