diff options
author | Bob Gilligan <gilligan@vyatta.com> | 2010-02-05 09:54:11 -0800 |
---|---|---|
committer | Bob Gilligan <gilligan@vyatta.com> | 2010-02-05 09:54:11 -0800 |
commit | e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f (patch) | |
tree | f4fc5aaccb4d9dac43ae7a825df9006532c1a059 /src | |
download | vyatta-biosdevname-e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f.tar.gz vyatta-biosdevname-e1971e4774a6ebb5ed33a09bdd60afa2c0534b6f.zip |
Initial commit.debian/0.1
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 44 | ||||
-rw-r--r-- | src/bios_dev_name.c | 139 | ||||
-rw-r--r-- | src/bios_dev_name.h | 21 | ||||
-rw-r--r-- | src/bios_device.c | 387 | ||||
-rw-r--r-- | src/bios_device.h | 35 | ||||
-rw-r--r-- | src/cistpl.h | 605 | ||||
-rw-r--r-- | src/dmidecode/config.h | 29 | ||||
-rw-r--r-- | src/dmidecode/dmidecode.c | 382 | ||||
-rw-r--r-- | src/dmidecode/dmidecode.h | 55 | ||||
-rw-r--r-- | src/dmidecode/dmioem.c | 120 | ||||
-rw-r--r-- | src/dmidecode/dmioem.h | 26 | ||||
-rw-r--r-- | src/dmidecode/types.h | 27 | ||||
-rw-r--r-- | src/dmidecode/util.c | 165 | ||||
-rw-r--r-- | src/dmidecode/util.h | 8 | ||||
-rw-r--r-- | src/eths.c | 218 | ||||
-rw-r--r-- | src/eths.h | 57 | ||||
-rw-r--r-- | src/ethtool-copy.h | 375 | ||||
-rw-r--r-- | src/ethtool-util.h | 22 | ||||
-rw-r--r-- | src/libbiosdevname.h | 29 | ||||
-rw-r--r-- | src/list.h | 422 | ||||
-rw-r--r-- | src/naming_policy.c | 107 | ||||
-rw-r--r-- | src/naming_policy.h | 14 | ||||
-rw-r--r-- | src/parse_cis.c | 882 | ||||
-rw-r--r-- | src/pci.c | 244 | ||||
-rw-r--r-- | src/pci.h | 53 | ||||
-rw-r--r-- | src/pcmcia.c | 295 | ||||
-rw-r--r-- | src/pcmcia.h | 35 | ||||
-rw-r--r-- | src/pirq.c | 150 | ||||
-rw-r--r-- | src/pirq.h | 58 | ||||
-rw-r--r-- | src/read-cis.c | 275 | ||||
-rw-r--r-- | src/read_proc.c | 108 | ||||
-rw-r--r-- | src/state.h | 18 |
32 files changed, 5405 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..5d3562c --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,44 @@ +INCLUDES = -I.. +AM_CFLAGS = -Wall -fno-strict-aliasing + +srcPath=src/ +man_MANS = biosdevname.1 + +sbin_PROGRAMS = src/biosdevname src/biosdevnameS +src_biosdevname_SOURCES = \ + src/bios_dev_name.c \ + src/bios_device.c \ + src/pirq.c \ + src/pci.c \ + src/eths.c \ + src/read_proc.c \ + src/naming_policy.c \ + src/pcmcia.c \ + src/read-cis.c \ + src/parse_cis.c \ + src/dmidecode/dmidecode.c \ + src/dmidecode/dmioem.c \ + src/dmidecode/util.c + +src_biosdevnameS_SOURCES = $(src_biosdevname_SOURCES) +src_biosdevnameS_LDFLAGS = -all-static + +EXTRA_DIST = \ + src/bios_dev_name.h \ + src/bios_device.h \ + src/pirq.h \ + src/pci.h \ + src/eths.h \ + src/ethtool-util.h \ + src/ethtool-copy.h \ + src/list.h \ + src/naming_policy.h \ + src/pcmcia.h \ + src/cistpl.h \ + src/state.h \ + src/libbiosdevname.h \ + src/dmidecode/config.h \ + src/dmidecode/dmidecode.h \ + src/dmidecode/dmioem.h \ + src/dmidecode/types.h \ + src/dmidecode/util.h diff --git a/src/bios_dev_name.c b/src/bios_dev_name.c new file mode 100644 index 0000000..ce13742 --- /dev/null +++ b/src/bios_dev_name.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> + +#include "libbiosdevname.h" +#include "bios_dev_name.h" + +static struct bios_dev_name_opts opts; + +static void usage(void) +{ + fprintf(stderr, "Usage: biosdevname [options] [args]...\n"); + fprintf(stderr, " Options:\n"); + fprintf(stderr, " -i or --interface treat [args] as ethernet devs\n"); + fprintf(stderr, " -d or --debug enable debugging\n"); + fprintf(stderr, " -n or --nosort don't sort the PCI device list breadth-first\n"); + fprintf(stderr, " --policy [kernelnames | all_ethN | all_names | embedded_ethN_slots_names]\n"); + fprintf(stderr, " Example: biosdevname -i eth0\n"); + fprintf(stderr, " returns: eth0\n"); + fprintf(stderr, " when the BIOS name and kernel name are both eth0.\n"); + fprintf(stderr, " --nosort implies --policy kernelnames.\n"); + fprintf(stderr, " You must be root to run this, as it must read from /dev/mem.\n"); +} + +static int +set_policy(const char *arg) +{ + int rc = all_ethN; + if (!strncmp("kernelnames", arg, sizeof("kernelnames"))) + rc = kernelnames; + else if (!strncmp("all_ethN", arg, sizeof("all_ethN"))) + rc = all_ethN; + else if (!strncmp("all_names", arg, sizeof("all_names"))) + rc = all_names; + else if (!strncmp("embedded_ethN_slots_names", arg, sizeof("embedded_ethN_slots_names"))) + rc = embedded_ethN_slots_names; + return rc; +} + +static void +parse_opts(int argc, char **argv) +{ + int c; + int option_index = 0; + + while (1) { + static struct option long_options[] = + /* name, has_arg, flag, val */ + { + {"debug", no_argument, 0, 'd'}, + {"interface", no_argument, 0, 'i'}, + {"nosort", no_argument, 0, 'n'}, + {"policy", required_argument, 0, 'p'}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, + "dinp:", + long_options, &option_index); + if (c == -1) + break; + switch(c) { + case 'd': + opts.debug = 1; + break; + case 'i': + opts.interface = 1; + break; + case 'n': + opts.sortroutine = nosort; + break; + case 'p': + opts.namingpolicy = set_policy(optarg); + break; + default: + usage(); + exit(1); + } + } + + if (optind < argc) { + opts.argc = argc-optind; + opts.argv = &argv[optind]; + opts.optind = optind; + } + + if (opts.sortroutine == nosort) + opts.namingpolicy = kernelnames; +} + +int main(int argc, char *argv[]) +{ + int i, rc=0; + char *name; + void *cookie = NULL; + + parse_opts(argc, argv); + cookie = setup_bios_devices(opts.sortroutine, opts.namingpolicy); + if (!cookie) { + usage(); + rc = 1; + goto out; + } + + if (opts.debug) { + unparse_bios_devices(cookie); + rc = 0; + goto out_cleanup; + } + + + if (!opts.interface) { + fprintf(stderr, "Unknown device type, try passing an option like -i\n"); + rc = 1; + goto out_usage; + } + + for (i=0; i<opts.argc; i++) { + name = kern_to_bios(cookie, opts.argv[i]); + if (name) { + printf("%s\n", name); + } + else + rc |= 2; /* one or more given devices weren't found */ + } + goto out_cleanup; + + out_usage: + usage(); + out_cleanup: + cleanup_bios_devices(cookie); + out: + return rc; +} diff --git a/src/bios_dev_name.h b/src/bios_dev_name.h new file mode 100644 index 0000000..658bfa3 --- /dev/null +++ b/src/bios_dev_name.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#ifndef GLUE_H_INCLUDED +#define GLUE_H_INCLUDED + +struct bios_dev_name_opts { + int argc; + char **argv; + int optind; + int sortroutine; + int namingpolicy; + unsigned int verbose:1; + unsigned int show_all:1; + unsigned int debug:1; + unsigned int interface:1; +}; + +#endif /* GLUE_H_INCLUDED */ 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; +} + diff --git a/src/bios_device.h b/src/bios_device.h new file mode 100644 index 0000000..8e76e1a --- /dev/null +++ b/src/bios_device.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#ifndef BIOS_DEVICE_H_INCLUDED +#define BIOS_DEVICE_H_INCLUDED + +#include <pci/pci.h> +#include "list.h" +#include "eths.h" +#include "pci.h" +#include "pcmcia.h" +#include "naming_policy.h" + + +struct bios_device { + struct list_head node; + struct network_device *netdev; + struct pci_device *pcidev; + struct pcmcia_device *pcmciadev; + char bios_name[IFNAMSIZ]; +}; + +static inline int is_pci(const struct bios_device *dev) +{ + return dev->pcidev != NULL; +} + +static inline int is_pcmcia(const struct bios_device *dev) +{ + return dev->pcmciadev != NULL; +} + +#endif /* BIOS_DEVICE_H_INCLUDED */ diff --git a/src/cistpl.h b/src/cistpl.h new file mode 100644 index 0000000..8a2f270 --- /dev/null +++ b/src/cistpl.h @@ -0,0 +1,605 @@ +/* + * cistpl.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds + */ + +#ifndef _LINUX_CISTPL_H +#define _LINUX_CISTPL_H + +#define CISTPL_NULL 0x00 +#define CISTPL_DEVICE 0x01 +#define CISTPL_LONGLINK_CB 0x02 +#define CISTPL_INDIRECT 0x03 +#define CISTPL_CONFIG_CB 0x04 +#define CISTPL_CFTABLE_ENTRY_CB 0x05 +#define CISTPL_LONGLINK_MFC 0x06 +#define CISTPL_BAR 0x07 +#define CISTPL_PWR_MGMNT 0x08 +#define CISTPL_EXTDEVICE 0x09 +#define CISTPL_CHECKSUM 0x10 +#define CISTPL_LONGLINK_A 0x11 +#define CISTPL_LONGLINK_C 0x12 +#define CISTPL_LINKTARGET 0x13 +#define CISTPL_NO_LINK 0x14 +#define CISTPL_VERS_1 0x15 +#define CISTPL_ALTSTR 0x16 +#define CISTPL_DEVICE_A 0x17 +#define CISTPL_JEDEC_C 0x18 +#define CISTPL_JEDEC_A 0x19 +#define CISTPL_CONFIG 0x1a +#define CISTPL_CFTABLE_ENTRY 0x1b +#define CISTPL_DEVICE_OC 0x1c +#define CISTPL_DEVICE_OA 0x1d +#define CISTPL_DEVICE_GEO 0x1e +#define CISTPL_DEVICE_GEO_A 0x1f +#define CISTPL_MANFID 0x20 +#define CISTPL_FUNCID 0x21 +#define CISTPL_FUNCE 0x22 +#define CISTPL_SWIL 0x23 +#define CISTPL_END 0xff +/* Layer 2 tuples */ +#define CISTPL_VERS_2 0x40 +#define CISTPL_FORMAT 0x41 +#define CISTPL_GEOMETRY 0x42 +#define CISTPL_BYTEORDER 0x43 +#define CISTPL_DATE 0x44 +#define CISTPL_BATTERY 0x45 +#define CISTPL_FORMAT_A 0x47 +/* Layer 3 tuples */ +#define CISTPL_ORG 0x46 +#define CISTPL_SPCL 0x90 + +typedef struct cistpl_longlink_t { + unsigned int addr; +} cistpl_longlink_t; + +typedef struct cistpl_checksum_t { + unsigned short addr; + unsigned short len; + unsigned char sum; +} cistpl_checksum_t; + +#define CISTPL_MAX_FUNCTIONS 8 +#define CISTPL_MFC_ATTR 0x00 +#define CISTPL_MFC_COMMON 0x01 + +typedef struct cistpl_longlink_mfc_t { + unsigned char nfn; + struct { + unsigned char space; + unsigned int addr; + } fn[CISTPL_MAX_FUNCTIONS]; +} cistpl_longlink_mfc_t; + +#define CISTPL_MAX_ALTSTR_STRINGS 4 + +typedef struct cistpl_altstr_t { + unsigned char ns; + unsigned char ofs[CISTPL_MAX_ALTSTR_STRINGS]; + char str[254]; +} cistpl_altstr_t; + +#define CISTPL_DTYPE_NULL 0x00 +#define CISTPL_DTYPE_ROM 0x01 +#define CISTPL_DTYPE_OTPROM 0x02 +#define CISTPL_DTYPE_EPROM 0x03 +#define CISTPL_DTYPE_EEPROM 0x04 +#define CISTPL_DTYPE_FLASH 0x05 +#define CISTPL_DTYPE_SRAM 0x06 +#define CISTPL_DTYPE_DRAM 0x07 +#define CISTPL_DTYPE_FUNCSPEC 0x0d +#define CISTPL_DTYPE_EXTEND 0x0e + +#define CISTPL_MAX_DEVICES 4 + +typedef struct cistpl_device_t { + unsigned char ndev; + struct { + unsigned char type; + unsigned char wp; + unsigned int speed; + unsigned int size; + } dev[CISTPL_MAX_DEVICES]; +} cistpl_device_t; + +#define CISTPL_DEVICE_MWAIT 0x01 +#define CISTPL_DEVICE_3VCC 0x02 + +typedef struct cistpl_device_o_t { + unsigned char flags; + cistpl_device_t device; +} cistpl_device_o_t; + +#define CISTPL_VERS_1_MAX_PROD_STRINGS 4 + +typedef struct cistpl_vers_1_t { + unsigned char major; + unsigned char minor; + unsigned char ns; + unsigned char ofs[CISTPL_VERS_1_MAX_PROD_STRINGS]; + char str[254]; +} cistpl_vers_1_t; + +typedef struct cistpl_jedec_t { + unsigned char nid; + struct { + unsigned char mfr; + unsigned char info; + } id[CISTPL_MAX_DEVICES]; +} cistpl_jedec_t; + +typedef struct cistpl_manfid_t { + unsigned short manf; + unsigned short card; +} cistpl_manfid_t; + +#define CISTPL_FUNCID_MULTI 0x00 +#define CISTPL_FUNCID_MEMORY 0x01 +#define CISTPL_FUNCID_SERIAL 0x02 +#define CISTPL_FUNCID_PARALLEL 0x03 +#define CISTPL_FUNCID_FIXED 0x04 +#define CISTPL_FUNCID_VIDEO 0x05 +#define CISTPL_FUNCID_NETWORK 0x06 +#define CISTPL_FUNCID_AIMS 0x07 +#define CISTPL_FUNCID_SCSI 0x08 + +#define CISTPL_SYSINIT_POST 0x01 +#define CISTPL_SYSINIT_ROM 0x02 + +typedef struct cistpl_funcid_t { + unsigned char func; + unsigned char sysinit; +} cistpl_funcid_t; + +typedef struct cistpl_funce_t { + unsigned char type; + unsigned char data[0]; +} cistpl_funce_t; + +/*====================================================================== + + Modem Function Extension Tuples + +======================================================================*/ + +#define CISTPL_FUNCE_SERIAL_IF 0x00 +#define CISTPL_FUNCE_SERIAL_CAP 0x01 +#define CISTPL_FUNCE_SERIAL_SERV_DATA 0x02 +#define CISTPL_FUNCE_SERIAL_SERV_FAX 0x03 +#define CISTPL_FUNCE_SERIAL_SERV_VOICE 0x04 +#define CISTPL_FUNCE_SERIAL_CAP_DATA 0x05 +#define CISTPL_FUNCE_SERIAL_CAP_FAX 0x06 +#define CISTPL_FUNCE_SERIAL_CAP_VOICE 0x07 +#define CISTPL_FUNCE_SERIAL_IF_DATA 0x08 +#define CISTPL_FUNCE_SERIAL_IF_FAX 0x09 +#define CISTPL_FUNCE_SERIAL_IF_VOICE 0x0a + +/* UART identification */ +#define CISTPL_SERIAL_UART_8250 0x00 +#define CISTPL_SERIAL_UART_16450 0x01 +#define CISTPL_SERIAL_UART_16550 0x02 +#define CISTPL_SERIAL_UART_8251 0x03 +#define CISTPL_SERIAL_UART_8530 0x04 +#define CISTPL_SERIAL_UART_85230 0x05 + +/* UART capabilities */ +#define CISTPL_SERIAL_UART_SPACE 0x01 +#define CISTPL_SERIAL_UART_MARK 0x02 +#define CISTPL_SERIAL_UART_ODD 0x04 +#define CISTPL_SERIAL_UART_EVEN 0x08 +#define CISTPL_SERIAL_UART_5BIT 0x01 +#define CISTPL_SERIAL_UART_6BIT 0x02 +#define CISTPL_SERIAL_UART_7BIT 0x04 +#define CISTPL_SERIAL_UART_8BIT 0x08 +#define CISTPL_SERIAL_UART_1STOP 0x10 +#define CISTPL_SERIAL_UART_MSTOP 0x20 +#define CISTPL_SERIAL_UART_2STOP 0x40 + +typedef struct cistpl_serial_t { + unsigned char uart_type; + unsigned char uart_cap_0; + unsigned char uart_cap_1; +} cistpl_serial_t; + +typedef struct cistpl_modem_cap_t { + unsigned char flow; + unsigned char cmd_buf; + unsigned char rcv_buf_0, rcv_buf_1, rcv_buf_2; + unsigned char xmit_buf_0, xmit_buf_1, xmit_buf_2; +} cistpl_modem_cap_t; + +#define CISTPL_SERIAL_MOD_103 0x01 +#define CISTPL_SERIAL_MOD_V21 0x02 +#define CISTPL_SERIAL_MOD_V23 0x04 +#define CISTPL_SERIAL_MOD_V22 0x08 +#define CISTPL_SERIAL_MOD_212A 0x10 +#define CISTPL_SERIAL_MOD_V22BIS 0x20 +#define CISTPL_SERIAL_MOD_V26 0x40 +#define CISTPL_SERIAL_MOD_V26BIS 0x80 +#define CISTPL_SERIAL_MOD_V27BIS 0x01 +#define CISTPL_SERIAL_MOD_V29 0x02 +#define CISTPL_SERIAL_MOD_V32 0x04 +#define CISTPL_SERIAL_MOD_V32BIS 0x08 +#define CISTPL_SERIAL_MOD_V34 0x10 + +#define CISTPL_SERIAL_ERR_MNP2_4 0x01 +#define CISTPL_SERIAL_ERR_V42_LAPM 0x02 + +#define CISTPL_SERIAL_CMPR_V42BIS 0x01 +#define CISTPL_SERIAL_CMPR_MNP5 0x02 + +#define CISTPL_SERIAL_CMD_AT1 0x01 +#define CISTPL_SERIAL_CMD_AT2 0x02 +#define CISTPL_SERIAL_CMD_AT3 0x04 +#define CISTPL_SERIAL_CMD_MNP_AT 0x08 +#define CISTPL_SERIAL_CMD_V25BIS 0x10 +#define CISTPL_SERIAL_CMD_V25A 0x20 +#define CISTPL_SERIAL_CMD_DMCL 0x40 + +typedef struct cistpl_data_serv_t { + unsigned char max_data_0; + unsigned char max_data_1; + unsigned char modulation_0; + unsigned char modulation_1; + unsigned char error_control; + unsigned char compression; + unsigned char cmd_protocol; + unsigned char escape; + unsigned char encrypt; + unsigned char misc_features; + unsigned char ccitt_code[0]; +} cistpl_data_serv_t; + +typedef struct cistpl_fax_serv_t { + unsigned char max_data_0; + unsigned char max_data_1; + unsigned char modulation; + unsigned char encrypt; + unsigned char features_0; + unsigned char features_1; + unsigned char ccitt_code[0]; +} cistpl_fax_serv_t; + +typedef struct cistpl_voice_serv_t { + unsigned char max_data_0; + unsigned char max_data_1; +} cistpl_voice_serv_t; + +/*====================================================================== + + LAN Function Extension Tuples + +======================================================================*/ + +#define CISTPL_FUNCE_LAN_TECH 0x01 +#define CISTPL_FUNCE_LAN_SPEED 0x02 +#define CISTPL_FUNCE_LAN_MEDIA 0x03 +#define CISTPL_FUNCE_LAN_NODE_ID 0x04 +#define CISTPL_FUNCE_LAN_CONNECTOR 0x05 + +/* LAN technologies */ +#define CISTPL_LAN_TECH_ARCNET 0x01 +#define CISTPL_LAN_TECH_ETHERNET 0x02 +#define CISTPL_LAN_TECH_TOKENRING 0x03 +#define CISTPL_LAN_TECH_LOCALTALK 0x04 +#define CISTPL_LAN_TECH_FDDI 0x05 +#define CISTPL_LAN_TECH_ATM 0x06 +#define CISTPL_LAN_TECH_WIRELESS 0x07 + +typedef struct cistpl_lan_tech_t { + unsigned char tech; +} cistpl_lan_tech_t; + +typedef struct cistpl_lan_speed_t { + unsigned int speed; +} cistpl_lan_speed_t; + +/* LAN media definitions */ +#define CISTPL_LAN_MEDIA_UTP 0x01 +#define CISTPL_LAN_MEDIA_STP 0x02 +#define CISTPL_LAN_MEDIA_THIN_COAX 0x03 +#define CISTPL_LAN_MEDIA_THICK_COAX 0x04 +#define CISTPL_LAN_MEDIA_FIBER 0x05 +#define CISTPL_LAN_MEDIA_900MHZ 0x06 +#define CISTPL_LAN_MEDIA_2GHZ 0x07 +#define CISTPL_LAN_MEDIA_5GHZ 0x08 +#define CISTPL_LAN_MEDIA_DIFF_IR 0x09 +#define CISTPL_LAN_MEDIA_PTP_IR 0x0a + +typedef struct cistpl_lan_media_t { + unsigned char media; +} cistpl_lan_media_t; + +typedef struct cistpl_lan_node_id_t { + unsigned char nb; + unsigned char id[16]; +} cistpl_lan_node_id_t; + +typedef struct cistpl_lan_connector_t { + unsigned char code; +} cistpl_lan_connector_t; + +/*====================================================================== + + IDE Function Extension Tuples + +======================================================================*/ + +#define CISTPL_IDE_INTERFACE 0x01 + +typedef struct cistpl_ide_interface_t { + unsigned char interface; +} cistpl_ide_interface_t; + +/* First feature byte */ +#define CISTPL_IDE_SILICON 0x04 +#define CISTPL_IDE_UNIQUE 0x08 +#define CISTPL_IDE_DUAL 0x10 + +/* Second feature byte */ +#define CISTPL_IDE_HAS_SLEEP 0x01 +#define CISTPL_IDE_HAS_STANDBY 0x02 +#define CISTPL_IDE_HAS_IDLE 0x04 +#define CISTPL_IDE_LOW_POWER 0x08 +#define CISTPL_IDE_REG_INHIBIT 0x10 +#define CISTPL_IDE_HAS_INDEX 0x20 +#define CISTPL_IDE_IOIS16 0x40 + +typedef struct cistpl_ide_feature_t { + unsigned char feature1; + unsigned char feature2; +} cistpl_ide_feature_t; + +#define CISTPL_FUNCE_IDE_IFACE 0x01 +#define CISTPL_FUNCE_IDE_MASTER 0x02 +#define CISTPL_FUNCE_IDE_SLAVE 0x03 + +/*====================================================================== + + Configuration Table Entries + +======================================================================*/ + +#define CISTPL_BAR_SPACE 0x07 +#define CISTPL_BAR_SPACE_IO 0x10 +#define CISTPL_BAR_PREFETCH 0x20 +#define CISTPL_BAR_CACHEABLE 0x40 +#define CISTPL_BAR_1MEG_MAP 0x80 + +typedef struct cistpl_bar_t { + unsigned char attr; + unsigned int size; +} cistpl_bar_t; + +typedef struct cistpl_config_t { + unsigned char last_idx; + unsigned int base; + unsigned int rmask[4]; + unsigned char subtuples; +} cistpl_config_t; + +/* These are bits in the 'present' field, and indices in 'param' */ +#define CISTPL_POWER_VNOM 0 +#define CISTPL_POWER_VMIN 1 +#define CISTPL_POWER_VMAX 2 +#define CISTPL_POWER_ISTATIC 3 +#define CISTPL_POWER_IAVG 4 +#define CISTPL_POWER_IPEAK 5 +#define CISTPL_POWER_IDOWN 6 + +#define CISTPL_POWER_HIGHZ_OK 0x01 +#define CISTPL_POWER_HIGHZ_REQ 0x02 + +typedef struct cistpl_power_t { + unsigned char present; + unsigned char flags; + unsigned int param[7]; +} cistpl_power_t; + +typedef struct cistpl_timing_t { + unsigned int wait, waitscale; + unsigned int ready, rdyscale; + unsigned int reserved, rsvscale; +} cistpl_timing_t; + +#define CISTPL_IO_LINES_MASK 0x1f +#define CISTPL_IO_8BIT 0x20 +#define CISTPL_IO_16BIT 0x40 +#define CISTPL_IO_RANGE 0x80 + +#define CISTPL_IO_MAX_WIN 16 + +typedef struct cistpl_io_t { + unsigned char flags; + unsigned char nwin; + struct { + unsigned int base; + unsigned int len; + } win[CISTPL_IO_MAX_WIN]; +} cistpl_io_t; + +typedef struct cistpl_irq_t { + unsigned int IRQInfo1; + unsigned int IRQInfo2; +} cistpl_irq_t; + +#define CISTPL_MEM_MAX_WIN 8 + +typedef struct cistpl_mem_t { + unsigned char flags; + unsigned char nwin; + struct { + unsigned int len; + unsigned int card_addr; + unsigned int host_addr; + } win[CISTPL_MEM_MAX_WIN]; +} cistpl_mem_t; + +#define CISTPL_CFTABLE_DEFAULT 0x0001 +#define CISTPL_CFTABLE_BVDS 0x0002 +#define CISTPL_CFTABLE_WP 0x0004 +#define CISTPL_CFTABLE_RDYBSY 0x0008 +#define CISTPL_CFTABLE_MWAIT 0x0010 +#define CISTPL_CFTABLE_AUDIO 0x0800 +#define CISTPL_CFTABLE_READONLY 0x1000 +#define CISTPL_CFTABLE_PWRDOWN 0x2000 + +typedef struct cistpl_cftable_entry_t { + unsigned char index; + unsigned short flags; + unsigned char interface; + cistpl_power_t vcc, vpp1, vpp2; + cistpl_timing_t timing; + cistpl_io_t io; + cistpl_irq_t irq; + cistpl_mem_t mem; + unsigned char subtuples; +} cistpl_cftable_entry_t; + +#define CISTPL_CFTABLE_MASTER 0x000100 +#define CISTPL_CFTABLE_INVALIDATE 0x000200 +#define CISTPL_CFTABLE_VGA_PALETTE 0x000400 +#define CISTPL_CFTABLE_PARITY 0x000800 +#define CISTPL_CFTABLE_WAIT 0x001000 +#define CISTPL_CFTABLE_SERR 0x002000 +#define CISTPL_CFTABLE_FAST_BACK 0x004000 +#define CISTPL_CFTABLE_BINARY_AUDIO 0x010000 +#define CISTPL_CFTABLE_PWM_AUDIO 0x020000 + +typedef struct cistpl_cftable_entry_cb_t { + unsigned char index; + unsigned int flags; + cistpl_power_t vcc, vpp1, vpp2; + unsigned char io; + cistpl_irq_t irq; + unsigned char mem; + unsigned char subtuples; +} cistpl_cftable_entry_cb_t; + +typedef struct cistpl_device_geo_t { + unsigned char ngeo; + struct { + unsigned char buswidth; + unsigned int erase_block; + unsigned int read_block; + unsigned int write_block; + unsigned int partition; + unsigned int interleave; + } geo[CISTPL_MAX_DEVICES]; +} cistpl_device_geo_t; + +typedef struct cistpl_vers_2_t { + unsigned char vers; + unsigned char comply; + unsigned short dindex; + unsigned char vspec8, vspec9; + unsigned char nhdr; + unsigned char vendor, info; + char str[244]; +} cistpl_vers_2_t; + +typedef struct cistpl_org_t { + unsigned char data_org; + char desc[30]; +} cistpl_org_t; + +#define CISTPL_ORG_FS 0x00 +#define CISTPL_ORG_APPSPEC 0x01 +#define CISTPL_ORG_XIP 0x02 + +typedef struct cistpl_format_t { + unsigned char type; + unsigned char edc; + unsigned int offset; + unsigned int length; +} cistpl_format_t; + +#define CISTPL_FORMAT_DISK 0x00 +#define CISTPL_FORMAT_MEM 0x01 + +#define CISTPL_EDC_NONE 0x00 +#define CISTPL_EDC_CKSUM 0x01 +#define CISTPL_EDC_CRC 0x02 +#define CISTPL_EDC_PCC 0x03 + +typedef union cisparse_t { + cistpl_device_t device; + cistpl_checksum_t checksum; + cistpl_longlink_t longlink; + cistpl_longlink_mfc_t longlink_mfc; + cistpl_vers_1_t version_1; + cistpl_altstr_t altstr; + cistpl_jedec_t jedec; + cistpl_manfid_t manfid; + cistpl_funcid_t funcid; + cistpl_funce_t funce; + cistpl_bar_t bar; + cistpl_config_t config; + cistpl_cftable_entry_t cftable_entry; + cistpl_cftable_entry_cb_t cftable_entry_cb; + cistpl_device_geo_t device_geo; + cistpl_vers_2_t vers_2; + cistpl_org_t org; + cistpl_format_t format; +} cisparse_t; + +typedef struct tuple_t { + unsigned int Attributes; + unsigned char DesiredTuple; + unsigned int Flags; /* internal use */ + unsigned int LinkOffset; /* internal use */ + unsigned int CISOffset; /* internal use */ + unsigned char TupleCode; + unsigned char TupleLink; + unsigned char TupleOffset; + unsigned char TupleDataMax; + unsigned char TupleDataLen; + unsigned char *TupleData; +} tuple_t; + +/* Special unsigned char value */ +#define RETURN_FIRST_TUPLE 0xff + +/* Attributes for tuple calls */ +#define TUPLE_RETURN_LINK 0x01 +#define TUPLE_RETURN_COMMON 0x02 + +/* For ValidateCIS */ +typedef struct cisinfo_t { + unsigned int Chains; +} cisinfo_t; + +#define CISTPL_MAX_CIS_SIZE 0x200 + +/* For ReplaceCIS */ +typedef struct cisdump_t { + unsigned int Length; + unsigned char Data[CISTPL_MAX_CIS_SIZE]; +} cisdump_t; + +typedef struct tuple_flags { + unsigned int link_space:4; + unsigned int has_link:1; + unsigned int mfc_fn:3; + unsigned int space:4; +} tuple_flags; + +#define BIND_FN_ALL 0xff + +extern int read_out_cis (unsigned int socket_no, FILE *fd); +extern int pcmcia_get_first_tuple(unsigned int function, tuple_t *tuple); +extern int pcmcia_get_next_tuple(unsigned int function, tuple_t *tuple); +extern int pcmcia_get_tuple_data(tuple_t *tuple); +extern int pccard_parse_tuple(tuple_t *tuple, cisparse_t *parse); +extern int parse_cis_one_socket(unsigned int socket_no); + +#endif /* LINUX_CISTPL_H */ diff --git a/src/dmidecode/config.h b/src/dmidecode/config.h new file mode 100644 index 0000000..52fd3c3 --- /dev/null +++ b/src/dmidecode/config.h @@ -0,0 +1,29 @@ +/* + * Configuration + */ + +#ifndef CONFIG_H +#define CONFIG_H + +/* Default memory device file */ +#ifdef __BEOS__ +#define DEFAULT_MEM_DEV "/dev/misc/mem" +#else +#ifdef __sun +#define DEFAULT_MEM_DEV "/dev/xsvc" +#else +#define DEFAULT_MEM_DEV "/dev/mem" +#endif +#endif + +/* Use mmap or not */ +#ifndef __BEOS__ +#define USE_MMAP +#endif + +/* Use memory alignment workaround or not */ +#ifdef __ia64__ +#define ALIGNMENT_WORKAROUND +#endif + +#endif diff --git a/src/dmidecode/dmidecode.c b/src/dmidecode/dmidecode.c new file mode 100644 index 0000000..5c542a6 --- /dev/null +++ b/src/dmidecode/dmidecode.c @@ -0,0 +1,382 @@ +/* + * Trimmed from DMI Decode http://savannah.nongnu.org/projects/dmidecode + * + * (C) 2000-2002 Alan Cox <alan@redhat.com> + * (C) 2002-2007 Jean Delvare <khali@linux-fr.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "config.h" +#include "types.h" +#include "util.h" +#include "dmidecode.h" +#include "dmioem.h" +#include "../state.h" +#include "../pci.h" + +static const char *out_of_spec = "<OUT OF SPEC>"; +static const char *bad_index = "<BAD INDEX>"; + +/* + * Type-independant Stuff + */ + +const char *dmi_string(struct dmi_header *dm, u8 s) +{ + char *bp=(char *)dm->data; + size_t i, len; + + if(s==0) + return "Not Specified"; + + bp+=dm->length; + while(s>1 && *bp) + { + bp+=strlen(bp); + bp++; + s--; + } + + if(!*bp) + return bad_index; + + /* ASCII filtering */ + len=strlen(bp); + for(i=0; i<len; i++) + if(bp[i]<32 || bp[i]==127) + bp[i]='.'; + + return bp; +} + +static const char *dmi_slot_current_usage(u8 code) +{ + /* 3.3.10.3 */ + static const char *usage[]={ + "Other", /* 0x01 */ + "Unknown", + "Available", + "In Use" /* 0x04 */ + }; + + if(code>=0x01 && code<=0x04) + return usage[code-0x01]; + return out_of_spec; +} + +static void dmi_slot_segment_bus_func(u16 code1, u8 code2, u8 code3, u8 type, const char *prefix) +{ + /* 3.3.10.8 */ + if (!(code1==0xFFFF && code2==0xFF && code3==0xFF)) + printf("%sSegment Group %u, Bus %u, Device %u, Function %u ", + prefix, code1, code2, (code3>>3)&0x1F, (code3&0x7)); + switch(type) + { + case 0x06: /* PCI */ + case 0x0E: /* PCI */ + case 0x0F: /* AGP */ + case 0x10: /* AGP */ + case 0x11: /* AGP */ + case 0x12: /* PCI-X */ + case 0x13: /* AGP */ + case 0xA5: /* PCI Express */ + printf("\n"); + break; + default: + if (code1 != 0xFF || code2 != 0xFF || code3 != 0xFF) + printf("%s\n", out_of_spec); + break; + } +} + +static u8 onboard_device_type(u8 code, const char *prefix) +{ + /* 3.3.x.2 */ + u8 e = (code & 0x80)>>7; + static const char *type[]={ + "Other", /* 1 */ + "Unknown", + "Video", + "SCSI Controller", + "Ethernet", + "Token Ring", + "Sound", + "PATA Controller", + "SATA Controller", + "SAS Controller" /* 0x0A */ + }; + code = code & 0x7F; + if(code>=0x01 && code<=0x0A) { + printf("%sStatus: %s\n", prefix, e?"Enabled":"Disabled"); + printf("%sDevice Type: %s\n", prefix, type[code-0x01]); + } + else + printf("%sDevice Type: %s\n", prefix, out_of_spec); +} + +/* + * Main + */ + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +static void dmi_decode(struct dmi_header *h, u16 ver, const struct libbiosdevname_state *state) +{ + u8 *data=h->data; + int domain, bus, device, function; + struct pci_device *pdev; + switch(h->type) + { + case 9: /* 3.3.10 System Slots */ + if (h->length >= 0x0E && h->length >=0x11) { + domain = WORD(data+0x0D); + bus = data[0x0F]; + device = (data[0x10]>>3)&0x1F; + function = data[0x10] & 7; + if (! (domain == 0xFFFF && bus == 0xFF && data[0x10] == 0xFF)) { + device = (data[0x10]>>3)&0x1F; + function = data[0x10] & 7; + pdev = find_pci_dev_by_pci_addr(state, domain, bus, device, function); + if (pdev) { + pdev->physical_slot = WORD(data+0x09); + pdev->smbios_type = 0; + pdev->smbios_instance = 0; + } + } + } + break; + case 41: /* 3.3.xx Onboard Device Information */ + domain = WORD(data+0x07); + bus = data[0x09]; + device = (data[0xa]>>3) & 0x1F; + function = data[0xa] & 0x7; + pdev = find_pci_dev_by_pci_addr(state, domain, bus, device, function); + if (pdev) { + pdev->physical_slot = 0; + pdev->smbios_enabled = !!(data[0x05] & 0x80); + pdev->smbios_type = data[0x05] & 0x7F; + pdev->smbios_instance = data[0x06]; + } + break; + + default: + if(dmi_decode_oem(h, state)) + break; + } +} + +static void to_dmi_header(struct dmi_header *h, u8 *data) +{ + h->type=data[0]; + h->length=data[1]; + h->handle=WORD(data+2); + h->data=data; +} + +static void dmi_table(u32 base, u16 len, u16 num, u16 ver, const char *devmem, const struct libbiosdevname_state *state) +{ + u8 *buf; + u8 *data; + int i=0; + + if((buf=mem_chunk(base, len, devmem))==NULL) + { +#ifndef USE_MMAP + printf("Table is unreachable, sorry. Try compiling dmidecode with -DUSE_MMAP.\n"); +#endif + return; + } + + data=buf; + while(i<num && data+4<=buf+len) /* 4 is the length of an SMBIOS structure header */ + { + u8 *next; + struct dmi_header h; + + to_dmi_header(&h, data); + + /* + * If a short entry is found (less than 4 bytes), not only it + * is invalid, but we cannot reliably locate the next entry. + * Better stop at this point, and let the user know his/her + * table is broken. + */ + if(h.length<4) + break; + + /* assign vendor for vendor-specific decodes later */ + if(h.type==0 && h.length>=5) + dmi_set_vendor(dmi_string(&h, data[0x04])); + + /* look for the next handle */ + next=data+h.length; + while(next-buf+1<len && (next[0]!=0 || next[1]!=0)) + next++; + next+=2; + if(next-buf<=len) + dmi_decode(&h, ver, state); + + data=next; + i++; + } + free(buf); +} + + +static int smbios_decode(u8 *buf, const char *devmem, const struct libbiosdevname_state *state) +{ + if(checksum(buf, buf[0x05]) + && memcmp(buf+0x10, "_DMI_", 5)==0 + && checksum(buf+0x10, 0x0F)) + { + dmi_table(DWORD(buf+0x18), WORD(buf+0x16), WORD(buf+0x1C), + (buf[0x06]<<8)+buf[0x07], devmem, state); + return 1; + } + + return 0; +} + +static int legacy_decode(u8 *buf, const char *devmem, const struct libbiosdevname_state *state) +{ + if(checksum(buf, 0x0F)) + { + dmi_table(DWORD(buf+0x08), WORD(buf+0x06), WORD(buf+0x0C), + ((buf[0x0E]&0xF0)<<4)+(buf[0x0E]&0x0F), devmem, state); + return 1; + } + + return 0; +} + +/* + * Probe for EFI interface + */ +#define EFI_NOT_FOUND (-1) +#define EFI_NO_SMBIOS (-2) +static int address_from_efi(size_t *address) +{ + FILE *efi_systab; + const char *filename; + char linebuf[64]; + int ret; + + *address=0; /* Prevent compiler warning */ + + /* + * Linux up to 2.6.6: /proc/efi/systab + * Linux 2.6.7 and up: /sys/firmware/efi/systab + */ + if((efi_systab=fopen(filename="/sys/firmware/efi/systab", "r"))==NULL + && (efi_systab=fopen(filename="/proc/efi/systab", "r"))==NULL) + { + /* No EFI interface, fallback to memory scan */ + return EFI_NOT_FOUND; + } + ret=EFI_NO_SMBIOS; + while((fgets(linebuf, sizeof(linebuf)-1, efi_systab))!=NULL) + { + char *addrp=strchr(linebuf, '='); + *(addrp++)='\0'; + if(strcmp(linebuf, "SMBIOS")==0) + { + *address=strtoul(addrp, NULL, 0); + ret=0; + break; + } + } + if(fclose(efi_systab)!=0) + perror(filename); + + if(ret==EFI_NO_SMBIOS) + fprintf(stderr, "%s: SMBIOS entry point missing\n", filename); + return ret; +} + +static const char *devmem = "/dev/mem"; + +int dmidecode_main(const struct libbiosdevname_state *state) +{ + int ret=0; /* Returned value */ + int found=0; + size_t fp; + int efi; + u8 *buf; + + /* First try EFI (ia64, Intel-based Mac) */ + efi=address_from_efi(&fp); + switch(efi) + { + case EFI_NOT_FOUND: + goto memory_scan; + case EFI_NO_SMBIOS: + ret=1; + goto exit_free; + } + + if((buf=mem_chunk(fp, 0x20, devmem))==NULL) + { + ret=1; + goto exit_free; + } + + if(smbios_decode(buf, devmem, state)) + found++; + goto done; + +memory_scan: + /* Fallback to memory scan (x86, x86_64) */ + if((buf=mem_chunk(0xF0000, 0x10000, devmem))==NULL) + { + ret=1; + goto exit_free; + } + + for(fp=0; fp<=0xFFF0; fp+=16) + { + if(memcmp(buf+fp, "_SM_", 4)==0 && fp<=0xFFE0) + { + if(smbios_decode(buf+fp, devmem, state)) + { + found++; + fp+=16; + } + } + else if(memcmp(buf+fp, "_DMI_", 5)==0) + { + if (legacy_decode(buf+fp, devmem, state)) + found++; + } + } + +done: + free(buf); + + +exit_free: + return ret; +} diff --git a/src/dmidecode/dmidecode.h b/src/dmidecode/dmidecode.h new file mode 100644 index 0000000..db8f7bd --- /dev/null +++ b/src/dmidecode/dmidecode.h @@ -0,0 +1,55 @@ +/* + * This file is part of the dmidecode project. + * + * (C) 2005-2007 Jean Delvare <khali@linux-fr.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +struct dmi_header +{ + u8 type; + u8 length; + u16 handle; + u8 *data; +}; + +const char *dmi_string(struct dmi_header *dm, u8 s); + +struct dmi_embedded_device +{ + u8 type; + u8 instance; + u16 domain; + u8 bus; + u8 device; + u8 function; + const char *reference_designation; +}; + +struct dmi_addon_device +{ + u8 type; + u8 instance; + u16 domain; + u8 bus; + u8 device; + u8 function; + const char *reference_designation; +}; + +struct libbiosdevname_state; +int dmidecode_main(const struct libbiosdevname_state *state); + diff --git a/src/dmidecode/dmioem.c b/src/dmidecode/dmioem.c new file mode 100644 index 0000000..217c13a --- /dev/null +++ b/src/dmidecode/dmioem.c @@ -0,0 +1,120 @@ +/* + * Decoding of OEM-specific entries + * This file is part of the dmidecode project. + * + * (C) 2007 Jean Delvare <khali@linux-fr.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <string.h> + +#include "types.h" +#include "dmidecode.h" +#include "dmioem.h" +#include "../pci.h" + +/* + * Globals for vendor-specific decodes + */ + +enum DMI_VENDORS { VENDOR_UNKNOWN, VENDOR_HP }; + +static enum DMI_VENDORS dmi_vendor=VENDOR_UNKNOWN; + +/* + * Remember the system vendor for later use. We only actually store the + * value if we know how to decode at least one specific entry type for + * that vendor. + */ +void dmi_set_vendor(const char *s) +{ + if(strcmp(s, "HP")==0) + dmi_vendor=VENDOR_HP; +} + +/* + * HP-specific data structures are decoded here. + * + * Code contributed by John Cagle. + */ + +static int dmi_decode_hp(struct dmi_header *h, const struct libbiosdevname_state *state) +{ + u8 *data=h->data; + int nic, ptr; + u8 smbios_type = 0; + u8 bus, device, func; + struct pci_device *pdev; + + switch(h->type) + { + case 209: + case 221: + /* + * Vendor Specific: HP ProLiant NIC MAC Information + * + * This prints the BIOS NIC number, + * PCI bus/device/function, and MAC address + */ + if (h->type == 221) + smbios_type=0; + else + smbios_type=0x05; + + nic=1; + ptr=4; + while(h->length>=ptr+8) + { + bus = data[ptr+1]; + device = data[ptr]>>3; + func = data[ptr]&7; + pdev = find_pci_dev_by_pci_addr(state, 0, bus, device, func); + if (pdev) { + if((data[ptr]==0x00 && data[ptr+1]==0x00) || + (data[ptr]==0xFF && data[ptr+1]==0xFF)) + pdev->smbios_enabled = 0; + else { + pdev->smbios_enabled = 1; + pdev->smbios_type = smbios_type; + pdev->smbios_instance = nic; + } + } + nic++; + ptr+=8; + } + break; + + default: + return 0; + } + return 1; +} + +/* + * Dispatch vendor-specific entries decoding + * Return 1 if decoding was successful, 0 otherwise + */ +int dmi_decode_oem(struct dmi_header *h, const struct libbiosdevname_state *state) +{ + switch(dmi_vendor) + { + case VENDOR_HP: + return dmi_decode_hp(h, state); + default: + return 0; + } +} diff --git a/src/dmidecode/dmioem.h b/src/dmidecode/dmioem.h new file mode 100644 index 0000000..bacdab7 --- /dev/null +++ b/src/dmidecode/dmioem.h @@ -0,0 +1,26 @@ +/* + * Decoding of OEM-specific entries + * This file is part of the dmidecode project. + * + * (C) 2007 Jean Delvare <khali@linux-fr.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../state.h" +struct dmi_header; + +void dmi_set_vendor(const char *s); +int dmi_decode_oem(struct dmi_header *h, const struct libbiosdevname_state *state); diff --git a/src/dmidecode/types.h b/src/dmidecode/types.h new file mode 100644 index 0000000..12f3c65 --- /dev/null +++ b/src/dmidecode/types.h @@ -0,0 +1,27 @@ +#ifndef TYPES_H +#define TYPES_H + +#include "config.h" + +typedef unsigned char u8; +typedef unsigned short u16; +typedef signed short i16; +typedef unsigned int u32; + +#ifdef ALIGNMENT_WORKAROUND +# ifdef BIGENDIAN +# define WORD(x) (u16)((x)[1]+((x)[0]<<8)) +# define DWORD(x) (u32)((x)[3]+((x)[2]<<8)+((x)[1]<<16)+((x)[0]<<24)) +# define QWORD(x) (U64(DWORD(x+4), DWORD(x))) +# else /* BIGENDIAN */ +# define WORD(x) (u16)((x)[0]+((x)[1]<<8)) +# define DWORD(x) (u32)((x)[0]+((x)[1]<<8)+((x)[2]<<16)+((x)[3]<<24)) +# define QWORD(x) (U64(DWORD(x), DWORD(x+4))) +# endif /* BIGENDIAN */ +#else /* ALIGNMENT_WORKAROUND */ +#define WORD(x) (u16)(*(const u16 *)(x)) +#define DWORD(x) (u32)(*(const u32 *)(x)) +#define QWORD(x) (*(const u64 *)(x)) +#endif /* ALIGNMENT_WORKAROUND */ + +#endif diff --git a/src/dmidecode/util.c b/src/dmidecode/util.c new file mode 100644 index 0000000..9eda714 --- /dev/null +++ b/src/dmidecode/util.c @@ -0,0 +1,165 @@ +/* + * Common "util" functions + * This file is part of the dmidecode project. + * + * (C) 2002-2005 Jean Delvare <khali@linux-fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include "config.h" + +#ifdef USE_MMAP +#include <sys/mman.h> +#ifndef MAP_FAILED +#define MAP_FAILED ((void *) -1) +#endif /* !MAP_FAILED */ +#endif /* USE MMAP */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> + +#include "types.h" +#include "util.h" + +#ifndef USE_MMAP +static int myread(int fd, u8 *buf, size_t count, const char *prefix) +{ + ssize_t r=1; + size_t r2=0; + + while(r2!=count && r!=0) + { + r=read(fd, buf+r2, count-r2); + if(r==-1) + { + if(errno!=EINTR) + { + close(fd); + perror(prefix); + return -1; + } + } + else + r2+=r; + } + + if(r2!=count) + { + close(fd); + fprintf(stderr, "%s: Unexpected end of file\n", prefix); + return -1; + } + + return 0; +} +#endif + +int checksum(const u8 *buf, size_t len) +{ + u8 sum=0; + size_t a; + + for(a=0; a<len; a++) + sum+=buf[a]; + return (sum==0); +} + +/* + * Copy a physical memory chunk into a memory buffer. + * This function allocates memory. + */ +void *mem_chunk(size_t base, size_t len, const char *devmem) +{ + void *p; + int fd; +#ifdef USE_MMAP + size_t mmoffset; + void *mmp; +#endif + + if((fd=open(devmem, O_RDONLY))==-1) + { + perror(devmem); + return NULL; + } + + if((p=malloc(len))==NULL) + { + perror("malloc"); + return NULL; + } + +#ifdef USE_MMAP +#ifdef _SC_PAGESIZE + mmoffset=base%sysconf(_SC_PAGESIZE); +#else + mmoffset=base%getpagesize(); +#endif /* _SC_PAGESIZE */ + /* + * Please note that we don't use mmap() for performance reasons here, + * but to workaround problems many people encountered when trying + * to read from /dev/mem using regular read() calls. + */ + mmp=mmap(0, mmoffset+len, PROT_READ, MAP_SHARED, fd, base-mmoffset); + if(mmp==MAP_FAILED) + { + fprintf(stderr, "%s: ", devmem); + perror("mmap"); + free(p); + return NULL; + } + + memcpy(p, (u8 *)mmp+mmoffset, len); + + if(munmap(mmp, mmoffset+len)==-1) + { + fprintf(stderr, "%s: ", devmem); + perror("munmap"); + } +#else /* USE_MMAP */ + if(lseek(fd, base, SEEK_SET)==-1) + { + fprintf(stderr, "%s: ", devmem); + perror("lseek"); + free(p); + return NULL; + } + + if(myread(fd, p, len, devmem)==-1) + { + free(p); + return NULL; + } +#endif /* USE_MMAP */ + + if(close(fd)==-1) + perror(devmem); + + return p; +} diff --git a/src/dmidecode/util.h b/src/dmidecode/util.h new file mode 100644 index 0000000..b546f64 --- /dev/null +++ b/src/dmidecode/util.h @@ -0,0 +1,8 @@ +#include <sys/types.h> + +#include "types.h" + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) + +int checksum(const u8 *buf, size_t len); +void *mem_chunk(size_t base, size_t len, const char *devmem); diff --git a/src/eths.c b/src/eths.c new file mode 100644 index 0000000..6ad5878 --- /dev/null +++ b/src/eths.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#define _GNU_SOURCE + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <getopt.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <linux/sockios.h> +#include <unistd.h> +#include <net/if.h> +#include <net/if_arp.h> +#include "ethtool-util.h" +#include "pci.h" +#include "eths.h" +#include "state.h" + +/* Display an Ethernet address in readable format. */ +char *pr_ether(char *buf, const int size, const unsigned char *s) +{ + snprintf(buf, size, "%02X:%02X:%02X:%02X:%02X:%02X", + (s[0] & 0377), (s[1] & 0377), (s[2] & 0377), + (s[3] & 0377), (s[4] & 0377), (s[5] & 0377) + ); + return (buf); +} + +static int eths_get_hwaddr(const char *devname, unsigned char *buf, int size, int *type) +{ + int fd, err; + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)); + + /* Open control socket. */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot get control socket"); + return 1; + } + + err = ioctl(fd, SIOCGIFHWADDR, &ifr); + if (!err) { + memcpy(buf, ifr.ifr_hwaddr.sa_data, min(size, sizeof(ifr.ifr_hwaddr.sa_data))); + *type = ifr.ifr_hwaddr.sa_family; + } + else { + perror("SIOCGIFHWADDR failed"); + } + close(fd); + return err; +} + +static int eths_get_info(const char *devname, struct ethtool_drvinfo *drvinfo) +{ + int fd, err; + struct ifreq ifr; + + /* Setup our control structures. */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)); + + /* Open control socket. */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot get control socket"); + return 1; + } + + drvinfo->cmd = ETHTOOL_GDRVINFO; + ifr.ifr_data = (caddr_t)drvinfo; + err = ioctl(fd, SIOCETHTOOL, &ifr); + close(fd); + return err; +} + +static int eths_get_permaddr(const char *devname, unsigned char *buf, int size) +{ + int fd, err; + struct ifreq ifr; + struct ethtool_perm_addr *permaddr; + int s = sizeof(*permaddr) + MAX_ADDR_LEN; + + permaddr = malloc(s); + if (!permaddr) + return 1; + memset(permaddr, 0, s); + + /* Setup our control structures. */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, devname, sizeof(ifr.ifr_name)); + + + + /* Open control socket. */ + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + perror("Cannot get control socket"); + return 1; + } + + permaddr->cmd = ETHTOOL_GPERMADDR; + permaddr->size = MAX_ADDR_LEN; + ifr.ifr_data = (caddr_t)permaddr; + err = ioctl(fd, SIOCETHTOOL, &ifr); + if (err < 0) { + close(fd); + return err; + } + memcpy(buf, permaddr->data, min(permaddr->size, size)); + free(permaddr); + close(fd); + return err; +} + +static void fill_eth_dev(struct network_device *dev) +{ + int rc; + eths_get_hwaddr(dev->kernel_name, dev->dev_addr, sizeof(dev->dev_addr), &dev->arphrd_type); + eths_get_permaddr(dev->kernel_name, dev->perm_addr, sizeof(dev->perm_addr)); + rc = eths_get_info(dev->kernel_name, &dev->drvinfo); + if (rc == 0) + dev->drvinfo_valid = 1; +} + +void free_eths(struct libbiosdevname_state *state) +{ + struct network_device *pos, *next; + list_for_each_entry_safe(pos, next, &state->network_devices, node) { + list_del(&pos->node); + free(pos); + } +} + +/* read_proc.c */ +extern int get_interfaces(struct libbiosdevname_state *state); + +void get_eths(struct libbiosdevname_state *state) +{ + struct network_device *pos; + get_interfaces(state); + list_for_each_entry(pos, &state->network_devices, node) { + fill_eth_dev(pos); + } +} + +int zero_mac(const void *addr) +{ + char zero_mac[MAX_ADDR_LEN]; + memset(zero_mac, 0, sizeof(zero_mac)); + return !memcmp(zero_mac, addr, sizeof(zero_mac)); +} + + +int unparse_network_device(char *buf, const int size, struct network_device *dev) +{ + char buffer[40]; + char *s = buf; + s += snprintf(s, size-(s-buf), "Kernel name: %s\n", dev->kernel_name); + if (!zero_mac(dev->perm_addr)) + s += snprintf(s, size-(s-buf), "Permanant MAC: %s\n", pr_ether(buffer, sizeof(buffer), dev->perm_addr)); + s += snprintf(s, size-(s-buf), "Assigned MAC : %s\n", pr_ether(buffer, sizeof(buffer), dev->dev_addr)); + if (drvinfo_valid(dev)) { + s += snprintf(s, size-(s-buf), "Driver: %s\n", dev->drvinfo.driver); + s += snprintf(s, size-(s-buf), "Driver version: %s\n", dev->drvinfo.version); + s += snprintf(s, size-(s-buf), "Firmware version: %s\n", dev->drvinfo.fw_version); + s += snprintf(s, size-(s-buf), "Bus Info: %s\n", dev->drvinfo.bus_info); + } + return (s-buf); +}; + +struct network_device * find_net_device_by_bus_info(struct libbiosdevname_state *state, + const char *bus_info) +{ + struct network_device *n; + list_for_each_entry(n, &state->network_devices, node) { + if (!strncmp(n->drvinfo.bus_info, bus_info, sizeof(n->drvinfo.bus_info))) + return n; + } + return NULL; +} + +int is_ethernet(struct network_device *dev) +{ + int i; + int rc = 0; + + /* FIXME: /sys/class/net/$kernel_name/device will be a symlink if there's underlying hardware, + * or not exist if it's virtual. Try using that if this isn't good enough already. + */ + + /* No bus means not visible to BIOS */ + if (strncmp("N/A", dev->drvinfo.bus_info, sizeof(dev->drvinfo.bus_info)) == 0) + goto out; + + const char *nonethernet_drivers[] = { + "bridge", + "tun", + }; + for (i=0; i<sizeof(nonethernet_drivers)/sizeof(nonethernet_drivers[0]); i++) { + if (strncmp(dev->drvinfo.driver, nonethernet_drivers[i], sizeof(dev->drvinfo.driver)) == 0) + goto out; + } + + rc = dev->arphrd_type == ARPHRD_ETHER; +out: + return rc; +} diff --git a/src/eths.h b/src/eths.h new file mode 100644 index 0000000..09a20ad --- /dev/null +++ b/src/eths.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#ifndef __ETHS_H_INCLUDED +#define __ETHS_H_INCLUDED + +#include <net/if.h> +#include <net/if_arp.h> + +/* Bogus */ +#undef MAX_ADDR_LEN +#define MAX_ADDR_LEN 32 + +#include "list.h" +#include "ethtool-util.h" +#include "state.h" + +struct network_device { + struct list_head node; + char kernel_name[IFNAMSIZ]; /* ethN */ + unsigned char perm_addr[MAX_ADDR_LEN]; + unsigned char dev_addr[MAX_ADDR_LEN]; /* mutable MAC address, not unparsed */ + struct ethtool_drvinfo drvinfo; + int drvinfo_valid; + int arphrd_type; /* e.g. ARPHDR_ETHER */ + int hardware_claimed; /* true when recognized as PCI or PCMCIA and added to list of bios_devices */ +}; + +extern void get_eths(struct libbiosdevname_state *state); +extern void free_eths(struct libbiosdevname_state *state); +extern int unparse_network_device(char *buf, const int size, struct network_device *dev); +extern struct network_device * find_net_device_by_bus_info(struct libbiosdevname_state *state, + const char *bus_info); +extern int is_ethernet(struct network_device *dev); + +extern int zero_mac(const void *addr); + +#define min(a,b) ((a) <= (b) ? (a) : (b)) + +static inline void claim_netdev(struct network_device *dev) +{ + dev->hardware_claimed = 1; +} + +static inline int netdev_is_claimed(const struct network_device *dev) +{ + return dev->hardware_claimed != 0; +} + +static inline int drvinfo_valid(const struct network_device *dev) +{ + return dev->drvinfo_valid != 0; +} + +#endif /* __ETHS_H_INCLUDED */ diff --git a/src/ethtool-copy.h b/src/ethtool-copy.h new file mode 100644 index 0000000..2aec47c --- /dev/null +++ b/src/ethtool-copy.h @@ -0,0 +1,375 @@ +/* + * ethtool.h: Defines for Linux ethtool. + * + * Copyright (C) 1998 David S. Miller (davem@redhat.com) + * Copyright 2001 Jeff Garzik <jgarzik@mandrakesoft.com> + * Portions Copyright 2001 Sun Microsystems (thockin@sun.com) + * Portions Copyright 2002 Intel (eli.kupermann@intel.com, + * christopher.leech@intel.com, + * scott.feldman@intel.com) + */ + +#ifndef _LINUX_ETHTOOL_H +#define _LINUX_ETHTOOL_H + + +/* This should work for both 32 and 64 bit userland. */ +struct ethtool_cmd { + u32 cmd; + u32 supported; /* Features this interface supports */ + u32 advertising; /* Features this interface advertises */ + u16 speed; /* The forced speed, 10Mb, 100Mb, gigabit */ + u8 duplex; /* Duplex, half or full */ + u8 port; /* Which connector port */ + u8 phy_address; + u8 transceiver; /* Which tranceiver to use */ + u8 autoneg; /* Enable or disable autonegotiation */ + u32 maxtxpkt; /* Tx pkts before generating tx int */ + u32 maxrxpkt; /* Rx pkts before generating rx int */ + u32 reserved[4]; +}; + +#define ETHTOOL_BUSINFO_LEN 32 +/* these strings are set to whatever the driver author decides... */ +struct ethtool_drvinfo { + u32 cmd; + char driver[32]; /* driver short name, "tulip", "eepro100" */ + char version[32]; /* driver version string */ + char fw_version[32]; /* firmware version string, if applicable */ + char bus_info[ETHTOOL_BUSINFO_LEN]; /* Bus info for this IF. */ + /* For PCI devices, use pci_dev->slot_name. */ + char reserved1[32]; + char reserved2[16]; + u32 n_stats; /* number of u64's from ETHTOOL_GSTATS */ + u32 testinfo_len; + u32 eedump_len; /* Size of data from ETHTOOL_GEEPROM (bytes) */ + u32 regdump_len; /* Size of data from ETHTOOL_GREGS (bytes) */ +}; + +#define SOPASS_MAX 6 +/* wake-on-lan settings */ +struct ethtool_wolinfo { + u32 cmd; + u32 supported; + u32 wolopts; + u8 sopass[SOPASS_MAX]; /* SecureOn(tm) password */ +}; + +/* for passing single values */ +struct ethtool_value { + u32 cmd; + u32 data; +}; + +/* for passing big chunks of data */ +struct ethtool_regs { + u32 cmd; + u32 version; /* driver-specific, indicates different chips/revs */ + u32 len; /* bytes */ + u8 data[0]; +}; + +/* for passing EEPROM chunks */ +struct ethtool_eeprom { + u32 cmd; + u32 magic; + u32 offset; /* in bytes */ + u32 len; /* in bytes */ + u8 data[0]; +}; + +/* for configuring coalescing parameters of chip */ +struct ethtool_coalesce { + u32 cmd; /* ETHTOOL_{G,S}COALESCE */ + + /* How many usecs to delay an RX interrupt after + * a packet arrives. If 0, only rx_max_coalesced_frames + * is used. + */ + u32 rx_coalesce_usecs; + + /* How many packets to delay an RX interrupt after + * a packet arrives. If 0, only rx_coalesce_usecs is + * used. It is illegal to set both usecs and max frames + * to zero as this would cause RX interrupts to never be + * generated. + */ + u32 rx_max_coalesced_frames; + + /* Same as above two parameters, except that these values + * apply while an IRQ is being services by the host. Not + * all cards support this feature and the values are ignored + * in that case. + */ + u32 rx_coalesce_usecs_irq; + u32 rx_max_coalesced_frames_irq; + + /* How many usecs to delay a TX interrupt after + * a packet is sent. If 0, only tx_max_coalesced_frames + * is used. + */ + u32 tx_coalesce_usecs; + + /* How many packets to delay a TX interrupt after + * a packet is sent. If 0, only tx_coalesce_usecs is + * used. It is illegal to set both usecs and max frames + * to zero as this would cause TX interrupts to never be + * generated. + */ + u32 tx_max_coalesced_frames; + + /* Same as above two parameters, except that these values + * apply while an IRQ is being services by the host. Not + * all cards support this feature and the values are ignored + * in that case. + */ + u32 tx_coalesce_usecs_irq; + u32 tx_max_coalesced_frames_irq; + + /* How many usecs to delay in-memory statistics + * block updates. Some drivers do not have an in-memory + * statistic block, and in such cases this value is ignored. + * This value must not be zero. + */ + u32 stats_block_coalesce_usecs; + + /* Adaptive RX/TX coalescing is an algorithm implemented by + * some drivers to improve latency under low packet rates and + * improve throughput under high packet rates. Some drivers + * only implement one of RX or TX adaptive coalescing. Anything + * not implemented by the driver causes these values to be + * silently ignored. + */ + u32 use_adaptive_rx_coalesce; + u32 use_adaptive_tx_coalesce; + + /* When the packet rate (measured in packets per second) + * is below pkt_rate_low, the {rx,tx}_*_low parameters are + * used. + */ + u32 pkt_rate_low; + u32 rx_coalesce_usecs_low; + u32 rx_max_coalesced_frames_low; + u32 tx_coalesce_usecs_low; + u32 tx_max_coalesced_frames_low; + + /* When the packet rate is below pkt_rate_high but above + * pkt_rate_low (both measured in packets per second) the + * normal {rx,tx}_* coalescing parameters are used. + */ + + /* When the packet rate is (measured in packets per second) + * is above pkt_rate_high, the {rx,tx}_*_high parameters are + * used. + */ + u32 pkt_rate_high; + u32 rx_coalesce_usecs_high; + u32 rx_max_coalesced_frames_high; + u32 tx_coalesce_usecs_high; + u32 tx_max_coalesced_frames_high; + + /* How often to do adaptive coalescing packet rate sampling, + * measured in seconds. Must not be zero. + */ + u32 rate_sample_interval; +}; + +/* for configuring RX/TX ring parameters */ +struct ethtool_ringparam { + u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */ + + /* Read only attributes. These indicate the maximum number + * of pending RX/TX ring entries the driver will allow the + * user to set. + */ + u32 rx_max_pending; + u32 rx_mini_max_pending; + u32 rx_jumbo_max_pending; + u32 tx_max_pending; + + /* Values changeable by the user. The valid values are + * in the range 1 to the "*_max_pending" counterpart above. + */ + u32 rx_pending; + u32 rx_mini_pending; + u32 rx_jumbo_pending; + u32 tx_pending; +}; + +/* for configuring link flow control parameters */ +struct ethtool_pauseparam { + u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */ + + /* If the link is being auto-negotiated (via ethtool_cmd.autoneg + * being true) the user may set 'autonet' here non-zero to have the + * pause parameters be auto-negotiated too. In such a case, the + * {rx,tx}_pause values below determine what capabilities are + * advertised. + * + * If 'autoneg' is zero or the link is not being auto-negotiated, + * then {rx,tx}_pause force the driver to use/not-use pause + * flow control. + */ + u32 autoneg; + u32 rx_pause; + u32 tx_pause; +}; + +#define ETH_GSTRING_LEN 32 +enum ethtool_stringset { + ETH_SS_TEST = 0, + ETH_SS_STATS, +}; + +/* for passing string sets for data tagging */ +struct ethtool_gstrings { + u32 cmd; /* ETHTOOL_GSTRINGS */ + u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/ + u32 len; /* number of strings in the string set */ + u8 data[0]; +}; + +enum ethtool_test_flags { + ETH_TEST_FL_OFFLINE = (1 << 0), /* online / offline */ + ETH_TEST_FL_FAILED = (1 << 1), /* test passed / failed */ +}; + +/* for requesting NIC test and getting results*/ +struct ethtool_test { + u32 cmd; /* ETHTOOL_TEST */ + u32 flags; /* ETH_TEST_FL_xxx */ + u32 reserved; + u32 len; /* result length, in number of u64 elements */ + u64 data[0]; +}; + +/* for dumping NIC-specific statistics */ +struct ethtool_stats { + u32 cmd; /* ETHTOOL_GSTATS */ + u32 n_stats; /* number of u64's being returned */ + u64 data[0]; +}; + +struct ethtool_perm_addr { + u32 cmd; /* ETHTOOL_GPERMADDR */ + u32 size; + u8 data[0]; +}; + + + +/* CMDs currently supported */ +#define ETHTOOL_GSET 0x00000001 /* Get settings. */ +#define ETHTOOL_SSET 0x00000002 /* Set settings, privileged. */ +#define ETHTOOL_GDRVINFO 0x00000003 /* Get driver info. */ +#define ETHTOOL_GREGS 0x00000004 /* Get NIC registers, privileged. */ +#define ETHTOOL_GWOL 0x00000005 /* Get wake-on-lan options. */ +#define ETHTOOL_SWOL 0x00000006 /* Set wake-on-lan options, priv. */ +#define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */ +#define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level, priv. */ +#define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation, priv. */ +#define ETHTOOL_GLINK 0x0000000a /* Get link status (ethtool_value) */ +#define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */ +#define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data, priv. */ +#define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */ +#define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config, priv. */ +#define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */ +#define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters, priv. */ +#define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */ +#define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters, priv. */ +#define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */ +#define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */ +#define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */ +#define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */ +#define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable + * (ethtool_value) */ +#define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable + * (ethtool_value), priv. */ +#define ETHTOOL_TEST 0x0000001a /* execute NIC self-test, priv. */ +#define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ +#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ +#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ +#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */ +#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */ +#define ETHTOOL_GPERMADDR 0x00000020 /* Get permanent hardware address */ + +/* compatibility with older code */ +#define SPARC_ETH_GSET ETHTOOL_GSET +#define SPARC_ETH_SSET ETHTOOL_SSET + +/* Indicates what features are supported by the interface. */ +#define SUPPORTED_10baseT_Half (1 << 0) +#define SUPPORTED_10baseT_Full (1 << 1) +#define SUPPORTED_100baseT_Half (1 << 2) +#define SUPPORTED_100baseT_Full (1 << 3) +#define SUPPORTED_1000baseT_Half (1 << 4) +#define SUPPORTED_1000baseT_Full (1 << 5) +#define SUPPORTED_Autoneg (1 << 6) +#define SUPPORTED_TP (1 << 7) +#define SUPPORTED_AUI (1 << 8) +#define SUPPORTED_MII (1 << 9) +#define SUPPORTED_FIBRE (1 << 10) +#define SUPPORTED_BNC (1 << 11) +#define SUPPORTED_10000baseT_Full (1 << 12) + +/* Indicates what features are advertised by the interface. */ +#define ADVERTISED_10baseT_Half (1 << 0) +#define ADVERTISED_10baseT_Full (1 << 1) +#define ADVERTISED_100baseT_Half (1 << 2) +#define ADVERTISED_100baseT_Full (1 << 3) +#define ADVERTISED_1000baseT_Half (1 << 4) +#define ADVERTISED_1000baseT_Full (1 << 5) +#define ADVERTISED_Autoneg (1 << 6) +#define ADVERTISED_TP (1 << 7) +#define ADVERTISED_AUI (1 << 8) +#define ADVERTISED_MII (1 << 9) +#define ADVERTISED_FIBRE (1 << 10) +#define ADVERTISED_BNC (1 << 11) +#define ADVERTISED_10000baseT_Full (1 << 12) + +/* The following are all involved in forcing a particular link + * mode for the device for setting things. When getting the + * devices settings, these indicate the current mode and whether + * it was foced up into this mode or autonegotiated. + */ + +/* The forced speed, 10Mb, 100Mb, gigabit, 10GbE. */ +#define SPEED_10 10 +#define SPEED_100 100 +#define SPEED_1000 1000 +#define SPEED_10000 10000 + +/* Duplex, half or full. */ +#define DUPLEX_HALF 0x00 +#define DUPLEX_FULL 0x01 + +/* Which connector port. */ +#define PORT_TP 0x00 +#define PORT_AUI 0x01 +#define PORT_MII 0x02 +#define PORT_FIBRE 0x03 +#define PORT_BNC 0x04 + +/* Which tranceiver to use. */ +#define XCVR_INTERNAL 0x00 +#define XCVR_EXTERNAL 0x01 +#define XCVR_DUMMY1 0x02 +#define XCVR_DUMMY2 0x03 +#define XCVR_DUMMY3 0x04 + +/* Enable or disable autonegotiation. If this is set to enable, + * the forced link modes above are completely ignored. + */ +#define AUTONEG_DISABLE 0x00 +#define AUTONEG_ENABLE 0x01 + +/* Wake-On-Lan options. */ +#define WAKE_PHY (1 << 0) +#define WAKE_UCAST (1 << 1) +#define WAKE_MCAST (1 << 2) +#define WAKE_BCAST (1 << 3) +#define WAKE_ARP (1 << 4) +#define WAKE_MAGIC (1 << 5) +#define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ + +#endif /* _LINUX_ETHTOOL_H */ diff --git a/src/ethtool-util.h b/src/ethtool-util.h new file mode 100644 index 0000000..a724343 --- /dev/null +++ b/src/ethtool-util.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#ifndef ETHTOOL_UTIL_H__ +#define ETHTOOL_UTIL_H__ + +#include <pci/pci.h> + +#include <limits.h> +#if ULONG_MAX > 0xffffffff +typedef unsigned long u64; +#else +typedef unsigned long long u64; +#endif + +#include "ethtool-copy.h" + +int ethtool_get_info(const char *devname, struct ethtool_drvinfo *drvinfo); + +#endif diff --git a/src/libbiosdevname.h b/src/libbiosdevname.h new file mode 100644 index 0000000..c106a04 --- /dev/null +++ b/src/libbiosdevname.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#ifndef LIBBIOSDEVNAME_H_INCLUDED +#define LIBBIOSDEVNAME_H_INCLUDED + +enum sortroutine { + defaultsort, + nosort, +}; + +enum namingpolicy { + all_ethN, + all_names, + embedded_ethN_slots_names, + kernelnames, +}; + +extern void * setup_bios_devices(int sortroutine, int namingpolicy); +extern void cleanup_bios_devices(void *cookie); +extern char * kern_to_bios(void *cookie, const char *devname); +extern void unparse_bios_devices(void *cookie); +extern void unparse_bios_device_by_name(void *cookie, const char *name); + + + +#endif /* LIBBIOSDEVNAME_H_INCLUDED */ diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..6e9bfb8 --- /dev/null +++ b/src/list.h @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + * Copied from the Linux kernel version 2.6.18, modfied for use here. + */ + +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include <stddef.h> + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + + +/* + * Simple doubly linked list implementation, from Linux kernel 2.6.18 + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * Note: if 'old' was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue. + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +#endif /* _LINUX_LIST_H */ diff --git a/src/naming_policy.c b/src/naming_policy.c new file mode 100644 index 0000000..ffa33c8 --- /dev/null +++ b/src/naming_policy.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2006, 2007 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ + +#include <string.h> +#include <stdio.h> +#include <limits.h> +#include "bios_device.h" +#include "naming_policy.h" +#include "libbiosdevname.h" +#include "state.h" + +static void use_all_ethN(const struct libbiosdevname_state *state) +{ + struct bios_device *dev; + unsigned int i=0; + + list_for_each_entry(dev, &state->bios_devices, node) { + if (dev->netdev) + snprintf(dev->bios_name, sizeof(dev->bios_name), "eth%u", i++); + } +} + +static void use_kernel_names(const struct libbiosdevname_state *state) +{ + struct bios_device *dev; + + list_for_each_entry(dev, &state->bios_devices, node) { + if (dev->netdev) + strncpy(dev->bios_name, dev->netdev->kernel_name, sizeof(dev->bios_name)-1); + } +} + + +static void pcmcia_names(struct bios_device *dev) +{ + snprintf(dev->bios_name, sizeof(dev->bios_name), "eth_pccard_%u.%u", + dev->pcmciadev->socket, dev->pcmciadev->function); +} + +static void use_embedded_ethN_slots_names(const struct libbiosdevname_state *state) +{ + struct bios_device *dev; + unsigned int i=0; + + list_for_each_entry(dev, &state->bios_devices, node) { + if (is_pci(dev)) { + if (dev->pcidev->physical_slot == 0) + snprintf(dev->bios_name, sizeof(dev->bios_name), "eth%u", i++); + else if (dev->pcidev->physical_slot < INT_MAX) + snprintf(dev->bios_name, sizeof(dev->bios_name), "eth_s%d_%u", + dev->pcidev->physical_slot, + dev->pcidev->index_in_slot); + else if (dev->pcidev->physical_slot == INT_MAX) + snprintf(dev->bios_name, sizeof(dev->bios_name), "eth_unknown_%u", i++); + } + else if (is_pcmcia(dev)) + pcmcia_names(dev); + } +} + +static void use_all_names(const struct libbiosdevname_state *state) +{ + struct bios_device *dev; + unsigned int i=0; + + list_for_each_entry(dev, &state->bios_devices, node) { + if (is_pci(dev)) { + if (dev->pcidev->physical_slot < INT_MAX) + snprintf(dev->bios_name, sizeof(dev->bios_name), "eth_s%d_%u", + dev->pcidev->physical_slot, + dev->pcidev->index_in_slot); + else + snprintf(dev->bios_name, sizeof(dev->bios_name), "eth_unknown_%u", i++); + } + else if (is_pcmcia(dev)) + pcmcia_names(dev); + } +} + +int assign_bios_network_names(const struct libbiosdevname_state *state, int sort, int policy) +{ + if (sort != nosort) { + switch (policy) { + case all_ethN: + use_all_ethN(state); + break; + case embedded_ethN_slots_names: + use_embedded_ethN_slots_names(state); + break; + case all_names: + use_all_names(state); + break; + case kernelnames: + default: + use_kernel_names(state); + break; + } + } + else + use_kernel_names(state); + + return 0; +} + diff --git a/src/naming_policy.h b/src/naming_policy.h new file mode 100644 index 0000000..9d61b42 --- /dev/null +++ b/src/naming_policy.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#ifndef NAMING_POLICY_H_INCLUDED +#define NAMING_POLICY_H_INCLUDED + +#include "state.h" + +extern int assign_bios_network_names(const struct libbiosdevname_state *state, + int sortroutine, int namingpolicy); + +#endif /* NAMING_POLICY_H_INCLUDED */ diff --git a/src/parse_cis.c b/src/parse_cis.c new file mode 100644 index 0000000..b03d2dc --- /dev/null +++ b/src/parse_cis.c @@ -0,0 +1,882 @@ +/* + * cistpl.c -- 16-bit PCMCIA Card Information Structure parser + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds + */ + +/* Parsing routines for individual tuples */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <endian.h> +#include <byteswap.h> + +#include "cistpl.h" + +#define IRQ_INFO2_VALID 0x10 + +static const u_char mantissa[] = { + 10, 12, 13, 15, 20, 25, 30, 35, + 40, 45, 50, 55, 60, 70, 80, 90 +}; + +static const u_int exponent[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 +}; + +/* Convert an extended speed byte to a time in nanoseconds */ +#define SPEED_CVT(v) \ + (mantissa[(((v)>>3)&15)-1] * exponent[(v)&7] / 10) +/* Convert a power byte to a current in 0.1 microamps */ +#define POWER_CVT(v) \ + (mantissa[((v)>>3)&15] * exponent[(v)&7] / 10) +#define POWER_SCALE(v) (exponent[(v)&7]) + +#if __BYTE_ORDER == __BIG_ENDIAN +# define le32_to_cpu(value) bswap_32(value) +# define le16_to_cpu(value) bswap_16(value) +#else +# define le32_to_cpu(value) (value) +# define le16_to_cpu(value) (value) +#endif + +static int parse_device(tuple_t *tuple, cistpl_device_t *device) +{ + int i; + u_char scale; + u_char *p, *q; + + p = (u_char *) tuple->TupleData; + q = p + tuple->TupleDataLen; + + device->ndev = 0; + for (i = 0; i < CISTPL_MAX_DEVICES; i++) { + + if (*p == 0xff) + break; + + device->dev[i].type = (*p >> 4); + device->dev[i].wp = (*p & 0x08) ? 1 : 0; + switch (*p & 0x07) { + case 0: device->dev[i].speed = 0; break; + case 1: device->dev[i].speed = 250; break; + case 2: device->dev[i].speed = 200; break; + case 3: device->dev[i].speed = 150; break; + case 4: device->dev[i].speed = 100; break; + case 7: + if (++p == q) return -EINVAL; + device->dev[i].speed = SPEED_CVT(*p); + while (*p & 0x80) + if (++p == q) return -EINVAL; + break; + default: + return -EINVAL; + } + + if (++p == q) + return -EINVAL; + if (*p == 0xff) + break; + scale = *p & 7; + if (scale == 7) + return -EINVAL; + device->dev[i].size = ((*p >> 3) + 1) * (512 << (scale*2)); + device->ndev++; + if (++p == q) + break; + } + + return 0; +} + +/*====================================================================*/ + +static int parse_checksum(tuple_t *tuple, cistpl_checksum_t *csum) +{ + u_char *p; + if (tuple->TupleDataLen < 5) + return -EINVAL; + p = (u_char *)tuple->TupleData; + csum->addr = tuple->CISOffset+(short)le16_to_cpu(*(u_short *)p)-2; + csum->len = le16_to_cpu(*(u_short *)(p + 2)); + csum->sum = *(p+4); + return 0; +} + +/*====================================================================*/ + +static int parse_longlink(tuple_t *tuple, cistpl_longlink_t *link) +{ + if (tuple->TupleDataLen < 4) + return -EINVAL; + link->addr = le32_to_cpu(*(u_int *)tuple->TupleData); + return 0; +} + +/*====================================================================*/ + +static int parse_longlink_mfc(tuple_t *tuple, + cistpl_longlink_mfc_t *link) +{ + u_char *p; + int i; + + p = (u_char *)tuple->TupleData; + + link->nfn = *p; p++; + if (tuple->TupleDataLen <= link->nfn*5) + return -EINVAL; + for (i = 0; i < link->nfn; i++) { + link->fn[i].space = *p; p++; + link->fn[i].addr = le32_to_cpu(*(u_int *)p); p += 4; + } + return 0; +} + +/*====================================================================*/ + +static int parse_strings(u_char *p, u_char *q, int max, + char *s, u_char *ofs, u_char *found) +{ + int i, j, ns; + + if (p == q) + return -EINVAL; + ns = 0; j = 0; + for (i = 0; i < max; i++) { + if (*p == 0xff) + break; + ofs[i] = j; + ns++; + for (;;) { + s[j++] = (*p == 0xff) ? '\0' : *p; + if ((*p == '\0') || (*p == 0xff)) + break; + if (++p == q) + return -EINVAL; + } + if ((*p == 0xff) || (++p == q)) + break; + } + if (found) { + *found = ns; + return 0; + } else { + return (ns == max) ? 0 : -EINVAL; + } +} + +/*====================================================================*/ + +static int parse_vers_1(tuple_t *tuple, cistpl_vers_1_t *vers_1) +{ + u_char *p, *q; + + p = (u_char *) tuple->TupleData; + q = p + tuple->TupleDataLen; + + vers_1->major = *p; p++; + vers_1->minor = *p; p++; + if (p >= q) + return -EINVAL; + + return parse_strings(p, q, CISTPL_VERS_1_MAX_PROD_STRINGS, + vers_1->str, vers_1->ofs, &vers_1->ns); +} + +/*====================================================================*/ + +static int parse_altstr(tuple_t *tuple, cistpl_altstr_t *altstr) +{ + u_char *p, *q; + + p = (u_char *) tuple->TupleData; + q = p + tuple->TupleDataLen; + + return parse_strings(p, q, CISTPL_MAX_ALTSTR_STRINGS, + altstr->str, altstr->ofs, &altstr->ns); +} + +/*====================================================================*/ + +static int parse_jedec(tuple_t *tuple, cistpl_jedec_t *jedec) +{ + u_char *p, *q; + int nid; + + p = (u_char *)tuple->TupleData; + q = p + tuple->TupleDataLen; + + for (nid = 0; nid < CISTPL_MAX_DEVICES; nid++) { + if (p > q-2) + break; + jedec->id[nid].mfr = p[0]; + jedec->id[nid].info = p[1]; + p += 2; + } + jedec->nid = nid; + return 0; +} + +/*====================================================================*/ + +static int parse_manfid(tuple_t *tuple, cistpl_manfid_t *m) +{ + u_short *p; + if (tuple->TupleDataLen < 4) + return -EINVAL; + p = (u_short *)tuple->TupleData; + m->manf = le16_to_cpu(p[0]); + m->card = le16_to_cpu(p[1]); + return 0; +} + +/*====================================================================*/ + +static int parse_funcid(tuple_t *tuple, cistpl_funcid_t *f) +{ + u_char *p; + if (tuple->TupleDataLen < 2) + return -EINVAL; + p = (u_char *)tuple->TupleData; + f->func = p[0]; + f->sysinit = p[1]; + return 0; +} + +/*====================================================================*/ + +static int parse_funce(tuple_t *tuple, cistpl_funce_t *f) +{ + u_char *p; + int i; + if (tuple->TupleDataLen < 1) + return -EINVAL; + p = (u_char *)tuple->TupleData; + f->type = p[0]; + for (i = 1; i < tuple->TupleDataLen; i++) + f->data[i-1] = p[i]; + return 0; +} + +/*====================================================================*/ + +static int parse_config(tuple_t *tuple, cistpl_config_t *config) +{ + int rasz, rmsz, i; + u_char *p; + + p = (u_char *)tuple->TupleData; + rasz = *p & 0x03; + rmsz = (*p & 0x3c) >> 2; + if (tuple->TupleDataLen < rasz+rmsz+4) + return -EINVAL; + config->last_idx = *(++p); + p++; + config->base = 0; + for (i = 0; i <= rasz; i++) + config->base += p[i] << (8*i); + p += rasz+1; + for (i = 0; i < 4; i++) + config->rmask[i] = 0; + for (i = 0; i <= rmsz; i++) + config->rmask[i>>2] += p[i] << (8*(i%4)); + config->subtuples = tuple->TupleDataLen - (rasz+rmsz+4); + return 0; +} + +/*====================================================================== + + The following routines are all used to parse the nightmarish + config table entries. + +======================================================================*/ + +static u_char *parse_power(u_char *p, u_char *q, + cistpl_power_t *pwr) +{ + int i; + u_int scale; + + if (p == q) return NULL; + pwr->present = *p; + pwr->flags = 0; + p++; + for (i = 0; i < 7; i++) + if (pwr->present & (1<<i)) { + if (p == q) + return NULL; + pwr->param[i] = POWER_CVT(*p); + scale = POWER_SCALE(*p); + while (*p & 0x80) { + if (++p == q) + return NULL; + if ((*p & 0x7f) < 100) + pwr->param[i] += (*p & 0x7f) * scale / 100; + else if (*p == 0x7d) + pwr->flags |= CISTPL_POWER_HIGHZ_OK; + else if (*p == 0x7e) + pwr->param[i] = 0; + else if (*p == 0x7f) + pwr->flags |= CISTPL_POWER_HIGHZ_REQ; + else + return NULL; + } + p++; + } + return p; +} + +/*====================================================================*/ + +static u_char *parse_timing(u_char *p, u_char *q, + cistpl_timing_t *timing) +{ + u_char scale; + + if (p == q) + return NULL; + scale = *p; + if ((scale & 3) != 3) { + if (++p == q) + return NULL; + timing->wait = SPEED_CVT(*p); + timing->waitscale = exponent[scale & 3]; + } else + timing->wait = 0; + scale >>= 2; + if ((scale & 7) != 7) { + if (++p == q) + return NULL; + timing->ready = SPEED_CVT(*p); + timing->rdyscale = exponent[scale & 7]; + } else + timing->ready = 0; + scale >>= 3; + if (scale != 7) { + if (++p == q) + return NULL; + timing->reserved = SPEED_CVT(*p); + timing->rsvscale = exponent[scale]; + } else + timing->reserved = 0; + p++; + return p; +} + +/*====================================================================*/ + +static u_char *parse_io(u_char *p, u_char *q, cistpl_io_t *io) +{ + int i, j, bsz, lsz; + + if (p == q) return NULL; + io->flags = *p; + + if (!(*p & 0x80)) { + io->nwin = 1; + io->win[0].base = 0; + io->win[0].len = (1 << (io->flags & CISTPL_IO_LINES_MASK)); + return p+1; + } + + if (++p == q) + return NULL; + io->nwin = (*p & 0x0f) + 1; + bsz = (*p & 0x30) >> 4; + if (bsz == 3) + bsz++; + lsz = (*p & 0xc0) >> 6; + if (lsz == 3) + lsz++; + p++; + + for (i = 0; i < io->nwin; i++) { + io->win[i].base = 0; + io->win[i].len = 1; + for (j = 0; j < bsz; j++, p++) { + if (p == q) + return NULL; + io->win[i].base += *p << (j*8); + } + for (j = 0; j < lsz; j++, p++) { + if (p == q) + return NULL; + io->win[i].len += *p << (j*8); + } + } + return p; +} + +/*====================================================================*/ + +static u_char *parse_mem(u_char *p, u_char *q, cistpl_mem_t *mem) +{ + int i, j, asz, lsz, has_ha; + u_int len, ca, ha; + + if (p == q) + return NULL; + + mem->nwin = (*p & 0x07) + 1; + lsz = (*p & 0x18) >> 3; + asz = (*p & 0x60) >> 5; + has_ha = (*p & 0x80); + if (++p == q) + return NULL; + + for (i = 0; i < mem->nwin; i++) { + len = ca = ha = 0; + for (j = 0; j < lsz; j++, p++) { + if (p == q) + return NULL; + len += *p << (j*8); + } + for (j = 0; j < asz; j++, p++) { + if (p == q) + return NULL; + ca += *p << (j*8); + } + if (has_ha) + for (j = 0; j < asz; j++, p++) { + if (p == q) + return NULL; + ha += *p << (j*8); + } + mem->win[i].len = len << 8; + mem->win[i].card_addr = ca << 8; + mem->win[i].host_addr = ha << 8; + } + return p; +} + +/*====================================================================*/ + +static u_char *parse_irq(u_char *p, u_char *q, cistpl_irq_t *irq) +{ + if (p == q) + return NULL; + irq->IRQInfo1 = *p; p++; + if (irq->IRQInfo1 & IRQ_INFO2_VALID) { + if (p+2 > q) + return NULL; + irq->IRQInfo2 = (p[1]<<8) + p[0]; + p += 2; + } + return p; +} + +/*====================================================================*/ + +static int parse_cftable_entry(tuple_t *tuple, + cistpl_cftable_entry_t *entry) +{ + u_char *p, *q, features; + + p = tuple->TupleData; + q = p + tuple->TupleDataLen; + entry->index = *p & 0x3f; + entry->flags = 0; + if (*p & 0x40) + entry->flags |= CISTPL_CFTABLE_DEFAULT; + if (*p & 0x80) { + if (++p == q) + return -EINVAL; + if (*p & 0x10) + entry->flags |= CISTPL_CFTABLE_BVDS; + if (*p & 0x20) + entry->flags |= CISTPL_CFTABLE_WP; + if (*p & 0x40) + entry->flags |= CISTPL_CFTABLE_RDYBSY; + if (*p & 0x80) + entry->flags |= CISTPL_CFTABLE_MWAIT; + entry->interface = *p & 0x0f; + } else + entry->interface = 0; + + /* Process optional features */ + if (++p == q) + return -EINVAL; + features = *p; p++; + + /* Power options */ + if ((features & 3) > 0) { + p = parse_power(p, q, &entry->vcc); + if (p == NULL) + return -EINVAL; + } else + entry->vcc.present = 0; + if ((features & 3) > 1) { + p = parse_power(p, q, &entry->vpp1); + if (p == NULL) + return -EINVAL; + } else + entry->vpp1.present = 0; + if ((features & 3) > 2) { + p = parse_power(p, q, &entry->vpp2); + if (p == NULL) + return -EINVAL; + } else + entry->vpp2.present = 0; + + /* Timing options */ + if (features & 0x04) { + p = parse_timing(p, q, &entry->timing); + if (p == NULL) + return -EINVAL; + } else { + entry->timing.wait = 0; + entry->timing.ready = 0; + entry->timing.reserved = 0; + } + + /* I/O window options */ + if (features & 0x08) { + p = parse_io(p, q, &entry->io); + if (p == NULL) + return -EINVAL; + } else + entry->io.nwin = 0; + + /* Interrupt options */ + if (features & 0x10) { + p = parse_irq(p, q, &entry->irq); + if (p == NULL) return -EINVAL; + } else + entry->irq.IRQInfo1 = 0; + + switch (features & 0x60) { + case 0x00: + entry->mem.nwin = 0; + break; + case 0x20: + entry->mem.nwin = 1; + entry->mem.win[0].len = le16_to_cpu(*(u_short *)p) << 8; + entry->mem.win[0].card_addr = 0; + entry->mem.win[0].host_addr = 0; + p += 2; + if (p > q) + return -EINVAL; + break; + case 0x40: + entry->mem.nwin = 1; + entry->mem.win[0].len = le16_to_cpu(*(u_short *)p) << 8; + entry->mem.win[0].card_addr = + le16_to_cpu(*(u_short *)(p+2)) << 8; + entry->mem.win[0].host_addr = 0; + p += 4; + if (p > q) + return -EINVAL; + break; + case 0x60: + p = parse_mem(p, q, &entry->mem); + if (p == NULL) + return -EINVAL; + break; + } + + /* Misc features */ + if (features & 0x80) { + if (p == q) + return -EINVAL; + entry->flags |= (*p << 8); + while (*p & 0x80) + if (++p == q) + return -EINVAL; + p++; + } + + entry->subtuples = q-p; + + return 0; +} + +/*====================================================================*/ + +static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar) +{ + u_char *p; + if (tuple->TupleDataLen < 6) + return -EINVAL; + p = (u_char *)tuple->TupleData; + bar->attr = *p; + p += 2; + bar->size = le32_to_cpu(*(u_int *)p); + return 0; +} + +static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config) +{ + u_char *p; + + p = (u_char *)tuple->TupleData; + if ((*p != 3) || (tuple->TupleDataLen < 6)) + return -EINVAL; + config->last_idx = *(++p); + p++; + config->base = le32_to_cpu(*(u_int *)p); + config->subtuples = tuple->TupleDataLen - 6; + return 0; +} + +static int parse_cftable_entry_cb(tuple_t *tuple, + cistpl_cftable_entry_cb_t *entry) +{ + u_char *p, *q, features; + + p = tuple->TupleData; + q = p + tuple->TupleDataLen; + entry->index = *p & 0x3f; + entry->flags = 0; + if (*p & 0x40) + entry->flags |= CISTPL_CFTABLE_DEFAULT; + + /* Process optional features */ + if (++p == q) + return -EINVAL; + features = *p; p++; + + /* Power options */ + if ((features & 3) > 0) { + p = parse_power(p, q, &entry->vcc); + if (p == NULL) + return -EINVAL; + } else + entry->vcc.present = 0; + if ((features & 3) > 1) { + p = parse_power(p, q, &entry->vpp1); + if (p == NULL) + return -EINVAL; + } else + entry->vpp1.present = 0; + if ((features & 3) > 2) { + p = parse_power(p, q, &entry->vpp2); + if (p == NULL) + return -EINVAL; + } else + entry->vpp2.present = 0; + + /* I/O window options */ + if (features & 0x08) { + if (p == q) + return -EINVAL; + entry->io = *p; p++; + } else + entry->io = 0; + + /* Interrupt options */ + if (features & 0x10) { + p = parse_irq(p, q, &entry->irq); + if (p == NULL) + return -EINVAL; + } else + entry->irq.IRQInfo1 = 0; + + if (features & 0x20) { + if (p == q) + return -EINVAL; + entry->mem = *p; p++; + } else + entry->mem = 0; + + /* Misc features */ + if (features & 0x80) { + if (p == q) + return -EINVAL; + entry->flags |= (*p << 8); + if (*p & 0x80) { + if (++p == q) + return -EINVAL; + entry->flags |= (*p << 16); + } + while (*p & 0x80) + if (++p == q) + return -EINVAL; + p++; + } + + entry->subtuples = q-p; + + return 0; +} + +/*====================================================================*/ + +static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo) +{ + u_char *p, *q; + int n; + + p = (u_char *)tuple->TupleData; + q = p + tuple->TupleDataLen; + + for (n = 0; n < CISTPL_MAX_DEVICES; n++) { + if (p > q-6) + break; + geo->geo[n].buswidth = p[0]; + geo->geo[n].erase_block = 1 << (p[1]-1); + geo->geo[n].read_block = 1 << (p[2]-1); + geo->geo[n].write_block = 1 << (p[3]-1); + geo->geo[n].partition = 1 << (p[4]-1); + geo->geo[n].interleave = 1 << (p[5]-1); + p += 6; + } + geo->ngeo = n; + return 0; +} + +/*====================================================================*/ + +static int parse_vers_2(tuple_t *tuple, cistpl_vers_2_t *v2) +{ + u_char *p, *q; + + if (tuple->TupleDataLen < 10) + return -EINVAL; + + p = tuple->TupleData; + q = p + tuple->TupleDataLen; + + v2->vers = p[0]; + v2->comply = p[1]; + v2->dindex = le16_to_cpu(*(u_short *)(p+2)); + v2->vspec8 = p[6]; + v2->vspec9 = p[7]; + v2->nhdr = p[8]; + p += 9; + return parse_strings(p, q, 2, v2->str, &v2->vendor, NULL); +} + +/*====================================================================*/ + +static int parse_org(tuple_t *tuple, cistpl_org_t *org) +{ + u_char *p, *q; + int i; + + p = tuple->TupleData; + q = p + tuple->TupleDataLen; + if (p == q) + return -EINVAL; + org->data_org = *p; + if (++p == q) + return -EINVAL; + for (i = 0; i < 30; i++) { + org->desc[i] = *p; + if (*p == '\0') break; + if (++p == q) + return -EINVAL; + } + return 0; +} + +/*====================================================================*/ + +static int parse_format(tuple_t *tuple, cistpl_format_t *fmt) +{ + u_char *p; + + if (tuple->TupleDataLen < 10) + return -EINVAL; + + p = tuple->TupleData; + + fmt->type = p[0]; + fmt->edc = p[1]; + fmt->offset = le32_to_cpu(*(u_int *)(p+2)); + fmt->length = le32_to_cpu(*(u_int *)(p+6)); + + return 0; +} + +/*====================================================================*/ + +int pccard_parse_tuple(tuple_t *tuple, cisparse_t *parse) +{ + int ret = 0; + + if (tuple->TupleDataLen > tuple->TupleDataMax) + return -EINVAL; + switch (tuple->TupleCode) { + case CISTPL_DEVICE: + case CISTPL_DEVICE_A: + ret = parse_device(tuple, &parse->device); + break; + case CISTPL_BAR: + ret = parse_bar(tuple, &parse->bar); + break; + case CISTPL_CONFIG_CB: + ret = parse_config_cb(tuple, &parse->config); + break; + case CISTPL_CFTABLE_ENTRY_CB: + ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb); + break; + case CISTPL_CHECKSUM: + ret = parse_checksum(tuple, &parse->checksum); + break; + case CISTPL_LONGLINK_A: + case CISTPL_LONGLINK_C: + ret = parse_longlink(tuple, &parse->longlink); + break; + case CISTPL_LONGLINK_MFC: + ret = parse_longlink_mfc(tuple, &parse->longlink_mfc); + break; + case CISTPL_VERS_1: + ret = parse_vers_1(tuple, &parse->version_1); + break; + case CISTPL_ALTSTR: + ret = parse_altstr(tuple, &parse->altstr); + break; + case CISTPL_JEDEC_A: + case CISTPL_JEDEC_C: + ret = parse_jedec(tuple, &parse->jedec); + break; + case CISTPL_MANFID: + ret = parse_manfid(tuple, &parse->manfid); + break; + case CISTPL_FUNCID: + ret = parse_funcid(tuple, &parse->funcid); + break; + case CISTPL_FUNCE: + ret = parse_funce(tuple, &parse->funce); + break; + case CISTPL_CONFIG: + ret = parse_config(tuple, &parse->config); + break; + case CISTPL_CFTABLE_ENTRY: + ret = parse_cftable_entry(tuple, &parse->cftable_entry); + break; + case CISTPL_DEVICE_GEO: + case CISTPL_DEVICE_GEO_A: + ret = parse_device_geo(tuple, &parse->device_geo); + break; + case CISTPL_VERS_2: + ret = parse_vers_2(tuple, &parse->vers_2); + break; + case CISTPL_ORG: + ret = parse_org(tuple, &parse->org); + break; + case CISTPL_FORMAT: + case CISTPL_FORMAT_A: + ret = parse_format(tuple, &parse->format); + break; + case CISTPL_NO_LINK: + case CISTPL_LINKTARGET: + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} diff --git a/src/pci.c b/src/pci.c new file mode 100644 index 0000000..c5ae788 --- /dev/null +++ b/src/pci.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include "pirq.h" +#include <pci/pci.h> +#include "pci.h" + +static int +is_parent_bridge(struct pci_dev *p, unsigned int target_bus) +{ + unsigned int primary, secondary; + + if ( (pci_read_word(p, PCI_HEADER_TYPE) & 0x7f) != PCI_HEADER_TYPE_BRIDGE) + return 0; + + primary=pci_read_byte(p, PCI_PRIMARY_BUS); + secondary=pci_read_byte(p, PCI_SECONDARY_BUS); + + if (secondary != target_bus) + return 0; + + return 1; +} + +static struct pci_dev * +find_parent(struct pci_access *pacc, unsigned int target_bus) +{ + struct pci_dev *p; + + for (p=pacc->devices; p; p=p->next) + if (is_parent_bridge(p, target_bus)) + return p; + + return NULL; +} + +/* + * Check our parent in case the device itself isn't listed + * in the PCI IRQ Routing Table. This has a problem, as + * our parent bridge on a card may not be included + * in the $PIR table. In that case, it falls back to "unknown". + */ +static int pci_dev_to_slot(struct routing_table *table, struct pci_access *pacc, struct pci_dev *p) +{ + int rc; + rc = pirq_pci_dev_to_slot(table, p->bus, p->dev); + if (rc == INT_MAX) { + p = find_parent(pacc, p->bus); + if (p) + rc = pirq_pci_dev_to_slot(table, p->bus, p->dev); + } + return rc; +} + +static void add_pci_dev(struct libbiosdevname_state *state, + struct routing_table *table, + struct pci_access *pacc, struct pci_dev *p) +{ + struct pci_device *dev; + dev = malloc(sizeof(*dev)); + if (!dev) { + fprintf(stderr, "out of memory\n"); + return; + } + memset(dev, 0, sizeof(*dev)); + INIT_LIST_HEAD(&dev->node); + memcpy(&dev->pci_dev, p, sizeof(*p)); /* This doesn't allow us to call PCI functions though */ + dev->physical_slot = pci_dev_to_slot(table, pacc, p); + dev->class = pci_read_word(p, PCI_CLASS_DEVICE); + list_add(&dev->node, &state->pci_devices); +} + +void free_pci_devices(struct libbiosdevname_state *state) +{ + struct pci_device *pos, *next; + list_for_each_entry_safe(pos, next, &state->pci_devices, node) { + list_del(&pos->node); + free(pos); + } +} + +int get_pci_devices(struct libbiosdevname_state *state) +{ + struct pci_access *pacc; + struct pci_dev *p; + struct pci_device *dev; + struct routing_table *table; + int rc=0; + + pacc = pci_alloc(); + if (!pacc) + return rc; + + pci_init(pacc); + pci_scan_bus(pacc); + + table = pirq_alloc_read_table(); + if (!table) + goto out; + + for (p=pacc->devices; p; p=p->next) { + dev = find_dev_by_pci(state, p); + if (!dev) + add_pci_dev(state, table, pacc, p); + } + + pirq_free_table(table); +out: + pci_cleanup(pacc); + return rc; +} + + +static int parse_pci_name(const char *s, int *domain, int *bus, int *dev, int *func) +{ + int err; +/* The domain part was added in 2.6 kernels. Test for that first. */ + err = sscanf(s, "%x:%2x:%2x.%x", domain, bus, dev, func); + if (err != 4) { + err = sscanf(s, "%2x:%2x.%x", bus, dev, func); + if (err != 3) { + return 1; + } + } + return 0; +} + +int unparse_pci_name(char *buf, int size, const struct pci_dev *pdev) +{ + return snprintf(buf, size, "%04x:%02x:%02x.%d", + pci_domain_nr(pdev), pdev->bus, pdev->dev, pdev->func); +} + +static int unparse_location(char *buf, const int size, const int location) +{ + char *s = buf; + if (location == 0) + s += snprintf(s, size-(s-buf), "embedded"); + else if (location == INT_MAX) + s += snprintf(s, size-(s-buf), "unknown"); + else if (location > 0) + s += snprintf(s, size-(s-buf), "%u", location); + else + s += snprintf(s, size-(s-buf), "unknown"); + return (s-buf); +} + +static int unparse_smbios_type41_type(char *buf, const int size, const int type) +{ + char *s = buf; + const char *msg[] = {"Other", + "Unknown", + "Video", + "SCSI Controller", + "Ethernet", + "Token Ring", + "Sound", + "PATA Controller", + "SATA Controller", + "SAS Controller", + }; + if (type > 0 && type <= sizeof(msg)) + s += snprintf(s, size-(s-buf), "%s\n", msg[type-1]); + else + s += snprintf(s, size-(s-buf), "<OUT OF SPEC>\n"); + return (s-buf); +} + + +int unparse_pci_device(char *buf, const int size, const struct pci_device *p) +{ + char *s = buf; + s += snprintf(s, size-(s-buf), "PCI name : "); + s += unparse_pci_name(s, size-(s-buf), &p->pci_dev); + s += snprintf(s, size-(s-buf), "\n"); + s += snprintf(s, size-(s-buf), "PCI Slot : "); + s += unparse_location(s, size-(s-buf), p->physical_slot); + s += snprintf(s, size-(s-buf), "\n"); + if (p->smbios_type) { + s += snprintf(s, size-(s-buf), "SMBIOS Device Type: "); + s += unparse_smbios_type41_type(s, size-(s-buf), p->smbios_type); + s += snprintf(s, size-(s-buf), "SMBIOS Instance: %u\n", p->smbios_instance); + s += snprintf(s, size-(s-buf), "SMBIOS Enabled: %s\n", p->smbios_instance?"True":"False"); + } + return (s-buf); +} + +static int is_same_pci(const struct pci_dev *a, const struct pci_dev *b) +{ + if (pci_domain_nr(a) == pci_domain_nr(b) && + a->bus == b->bus && + a->dev == b->dev && + a->func == b->func) + return 1; + return 0; +} + +struct pci_device * find_dev_by_pci(const struct libbiosdevname_state *state, + const struct pci_dev *p) +{ + struct pci_device *dev; + list_for_each_entry(dev, &state->pci_devices, node) { + if (is_same_pci(p, &dev->pci_dev)) + return dev; + } + return NULL; +} + +struct pci_device * find_pci_dev_by_pci_addr(const struct libbiosdevname_state *state, + const int domain, const int bus, const int device, const int func) +{ + struct pci_device *dev; + struct pci_device p; + +#ifdef HAVE_STRUCT_PCI_DEV_DOMAIN + p.pci_dev.domain = domain; +#endif + p.pci_dev.bus = bus; + p.pci_dev.dev = device; + p.pci_dev.func = func; + + list_for_each_entry(dev, &state->pci_devices, node) { + if (is_same_pci(&p.pci_dev, &dev->pci_dev)) + return dev; + } + return NULL; +} + +struct pci_device * find_dev_by_pci_name(const struct libbiosdevname_state *state, + const char *s) +{ + int domain=0, bus=0, device=0, func=0; + if (parse_pci_name(s, &domain, &bus, &device, &func)) + return NULL; + + return find_pci_dev_by_pci_addr(state, domain, bus, device, func); +} diff --git a/src/pci.h b/src/pci.h new file mode 100644 index 0000000..fbd195a --- /dev/null +++ b/src/pci.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ + +#ifndef PCI_H_INCLUDED +#define PCI_H_INCLUDED + +#include <pci/pci.h> +#include "list.h" +#include "state.h" +#include "config.h" + +struct pci_device { + struct list_head node; + struct pci_dev pci_dev; + int physical_slot; + unsigned int index_in_slot; + unsigned short int class; + unsigned char smbios_type; + unsigned char smbios_instance; + unsigned char smbios_enabled; +}; + +extern int get_pci_devices(struct libbiosdevname_state *state); +extern void free_pci_devices(struct libbiosdevname_state *state); + +extern struct pci_device * find_dev_by_pci(const struct libbiosdevname_state *state, const struct pci_dev *p); +extern struct pci_device * find_pci_dev_by_pci_addr(const struct libbiosdevname_state *state, const int domain, const int bus, const int device, const int func); +extern struct pci_device * find_dev_by_pci_name(const struct libbiosdevname_state *state, const char *s); +extern int unparse_pci_device(char *buf, const int size, const struct pci_device *p); +extern int unparse_pci_name(char *buf, int size, const struct pci_dev *pdev); + +static inline int is_pci_network(struct pci_device *dev) +{ + return (dev->class & 0xFF00) == 0x0200; +} + +#ifdef HAVE_STRUCT_PCI_DEV_DOMAIN +static inline int pci_domain_nr(const struct pci_dev *dev) +{ + return dev->domain; +} +#else +static inline int pci_domain_nr(const struct pci_dev *dev) +{ + return 0; +} +#endif + + +#endif /* PCI_H_INCLUDED */ diff --git a/src/pcmcia.c b/src/pcmcia.c new file mode 100644 index 0000000..a2c2ff5 --- /dev/null +++ b/src/pcmcia.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * + * Partly based on tools from pcmciautils-014, which states + * in its header: + * (C) 2004-2005 Dominik Brodowski <linux@brodo.de> + * + * Partly based on cardctl.c from pcmcia-cs-3.2.7/cardmgr/, which states + * in its header: + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * Licensed under the terms of the GNU GPL License version 2. + */ + +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <libgen.h> +#include <linux/limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + + +#include "state.h" +#include "pcmcia.h" + +#define MAX_SOCKET 8 + +static unsigned char get_lan_tech(cistpl_funce_t *funce) +{ + cistpl_lan_tech_t *t; + + if (funce->type == CISTPL_FUNCE_LAN_TECH) { + t = (cistpl_lan_tech_t *)(funce->data); + return t->tech; + } + return 0; +} + +static unsigned char parse_tuple_for_network(tuple_t *tuple, cisparse_t *parse) +{ + static int func = 0; + unsigned char rc = 0; + + switch (tuple->TupleCode) { + case CISTPL_FUNCID: + func = parse->funcid.func; + break; + case CISTPL_FUNCE: + if (func == CISTPL_FUNCID_NETWORK) + rc = get_lan_tech(&parse->funce); + break; + default: + break; + } + return rc; +} + +int get_network_type(unsigned int socket_no, unsigned char *network_type) +{ + int ret = 0; + tuple_t tuple; + unsigned char buf[256]; + cisparse_t parse; + unsigned char n; + + memset(&tuple, 0, sizeof(tuple_t)); + + ret = read_out_cis(socket_no, NULL); + if (ret) + return (ret); + + tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON; + tuple.DesiredTuple = RETURN_FIRST_TUPLE; + + ret = pcmcia_get_first_tuple(BIND_FN_ALL, &tuple); + if (ret) + return (ret); + + while(tuple.TupleCode != CISTPL_END) { + tuple.TupleData = buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + + pcmcia_get_tuple_data(&tuple); + + ret = pccard_parse_tuple(&tuple, &parse); + if (ret == 0) { + n = parse_tuple_for_network(&tuple, &parse); + if (n) { + *network_type = n; + break; + } + } + + ret = pcmcia_get_next_tuple(BIND_FN_ALL, &tuple); + if (ret) + break; + } + + return (ret); +} + +/** + * sysfs_path_is_file: Check if the path supplied points to a file + * @path: path to validate + * Returns 0 if path points to file, 1 otherwise + * Copied from sysfsutils-2.1.0 (which is LGPL2.1 or later), relicensed GPLv2 for use here. + */ +static int sysfs_path_is_file(const char *path) +{ + struct stat astats; + + if (!path) { + errno = EINVAL; + return 1; + } + if ((lstat(path, &astats)) != 0) { + return 1; + } + if (S_ISREG(astats.st_mode)) + return 0; + + return 1; +} + + + +static int pccardctl_socket_exists(unsigned long socket_no) +{ + char file[PATH_MAX]; + + snprintf(file, sizeof(file), + "/sys/class/pcmcia_socket/pcmcia_socket%lu/card_insert", + socket_no); + + return (!(sysfs_path_is_file(file))); +} + +static int read_out_file(char * file, char **output) +{ + int ret; + char *result = NULL; + int fd; + unsigned long resultsize = 0; + ssize_t length = 0; + + + *output = NULL; + + resultsize = getpagesize() + 1; + result = malloc(resultsize); + if (!result) + return -ENOMEM; + memset(result, 0, resultsize); + + fd = open(file, O_RDONLY); + if (fd < 0) { + ret = -1; + goto free_out; + } + + length = read(fd, result, resultsize-1); + if (length < 0) { + close(fd); + ret = -1; + goto free_out; + } + result[length] = '\0'; + if (result[length-1] == '\n') + result[length-1] = '\0'; + *output = result; + ret = 0; + goto out; +free_out: + free(result); +out: + return ret; +} + +static int pccardctl_get_one_f(unsigned long socket_no, unsigned int dev, const char *in_file, unsigned int *result) +{ + char *value; + char file[PATH_MAX]; + int ret; + + snprintf(file, sizeof(file), "/sys/bus/pcmcia/devices/%lu.%u/%s", + socket_no, dev, in_file); + ret = read_out_file(file, &value); + if (ret || !value) + return -EINVAL; + + if (sscanf(value, "0x%X", result) != 1) + return -EIO; + return 0; +} + +static int pccardctl_get_one(unsigned long socket_no, const char *in_file, unsigned int *result) +{ + return pccardctl_get_one_f(socket_no, 0, in_file, result); +} + +static int alloc_pcmcia(struct libbiosdevname_state *state, + unsigned long int socket_no) +{ + char file[PATH_MAX]; + char dev_s[PATH_MAX]; + char *dev; + int ret, i; + int rc; + + if (!pccardctl_socket_exists(socket_no)) + return -ENODEV; + + snprintf(file, sizeof(file), "/sys/class/pcmcia_socket/pcmcia_socket%lu/device", socket_no); + ret = readlink(file, dev_s, sizeof(dev_s) - 1); + if (ret > 0) { + dev_s[ret]='\0'; + dev = basename(dev_s); + } else { + snprintf(file, sizeof(file), "/sys/class/pcmcia_socket/pcmcia_socket%lu", socket_no); + ret = readlink(file, dev_s, sizeof(dev_s) - 1); + if (ret <= 0) + return -ENODEV; + dev_s[ret]='\0'; + dev = basename(dirname(dev_s)); + } + + for (i=0; i<4; i++) { + unsigned int function; + unsigned int function_id; + struct pcmcia_device *pdev; + unsigned char network_type = 0; + + if (pccardctl_get_one_f(socket_no, i, "function", &function)) + continue; + + pdev = malloc(sizeof(*pdev)); + if (!pdev) + return -ENOMEM; + + INIT_LIST_HEAD(&pdev->node); + pdev->socket = socket_no; + pdev->function = i; + if (!pccardctl_get_one(socket_no, "func_id", &function_id)) + pdev->function_id = function_id; + + rc = get_network_type(socket_no, &network_type); + if (!rc) + pdev->network_type = network_type; + + list_add_tail(&pdev->node, &state->pcmcia_devices); + } + return 0; +} + +void free_pcmcia_devices(struct libbiosdevname_state *state) +{ + struct pcmcia_device *pos, *next; + list_for_each_entry_safe(pos, next, &state->pcmcia_devices, node) { + list_del(&pos->node); + free(pos); + } +} + +int get_pcmcia_devices(struct libbiosdevname_state *state) +{ + unsigned long int socket_no; + for (socket_no = 0; socket_no < MAX_SOCKET; socket_no++) { + alloc_pcmcia(state, socket_no); + } + return 0; +} + +int unparse_pcmcia_name(char *buf, int size, const struct pcmcia_device *pdev) +{ + return snprintf(buf, size, "%u.%u", pdev->socket, pdev->function); +} + +int unparse_pcmcia_device(char *buf, const int size, const struct pcmcia_device *p) +{ + char *s = buf; + s += snprintf(s, size-(s-buf), "PCMCIA location : "); + s += unparse_pcmcia_name(s, size-(s-buf), p); + s += snprintf(s, size-(s-buf), "\n"); + return (s-buf); +} + diff --git a/src/pcmcia.h b/src/pcmcia.h new file mode 100644 index 0000000..a8aea93 --- /dev/null +++ b/src/pcmcia.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ + +#ifndef PCMCIA_H_INCLUDED +#define PCMCIA_H_INCLUDED + +#include "list.h" +#include "cistpl.h" +#include "state.h" + +struct pcmcia_device { + struct list_head node; + unsigned int socket; + unsigned int function; + int function_id; + int network_type; +}; + +extern int get_pcmcia_devices(struct libbiosdevname_state *state); +extern void free_pcmcia_devices(struct libbiosdevname_state *state); +extern int unparse_pcmcia_name(char *buf, int size, const struct pcmcia_device *pdev); +extern int unparse_pcmcia_device(char *buf, const int size, const struct pcmcia_device *p); + +static inline int is_pcmcia_network(struct pcmcia_device *dev) +{ + return dev->function_id == CISTPL_FUNCID_NETWORK && + (dev->network_type == CISTPL_LAN_TECH_ETHERNET || + dev->network_type == CISTPL_LAN_TECH_WIRELESS); +} + + +#endif /* PCMCIA_H_INCLUDED */ diff --git a/src/pirq.c b/src/pirq.c new file mode 100644 index 0000000..a342ac3 --- /dev/null +++ b/src/pirq.c @@ -0,0 +1,150 @@ +/* + * PCI IRQ Routing Table dumper + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * + * Based on the description of said table found at + * http://www.microsoft.com/hwdev/busbios/pciirq.htm + * Licensed under the GNU General Public license, version 2. + */ + +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include <sys/mman.h> +#include "pirq.h" + +/* If unknown, use INT_MAX so they get sorted last */ +int pirq_pci_dev_to_slot(struct routing_table *table, int bus, int dev) +{ + int i, num_slots; + struct slot_entry *slot; + + if (!table) + return INT_MAX; + + num_slots = (table->size - 32) / sizeof(*slot); + for (i=0; i<num_slots; i++) { + slot = &table->slot[i]; + if (slot->bus == bus && + PCI_DEVICE(slot->device) == dev) + return slot->slot; + } + return INT_MAX; +} + + + +struct routing_table * pirq_alloc_read_table() +{ + struct routing_table *table = NULL; + uint16_t size = 0; + uint8_t checksum = 0; + int i; + void *mem; + off_t offset=0L; + int fd=open("/dev/mem", O_RDONLY); + + if(fd==-1) + { + perror("open(/dev/mem)"); + return NULL; + } + + mem = mmap(0, 64*1024, PROT_READ, MAP_SHARED, fd, 0xF0000L); + if (!mem) { + perror("mmap(/dev/mem)"); + goto out; + } + + while( offset < 0xFFFF) + { + if(memcmp(mem+offset, "$PIR", 4)==0) + { + table = (struct routing_table *)(mem+offset); + size = table->size; + /* quick sanity checks */ + /* Version must be 1.0 */ + if (! (table->version >> 8)==1 && + (table->version && 0xFF) == 0) break; + + table = malloc(size); + if (!table) break; + + memcpy(table, mem+offset, size); + for (i=0; i<size; i++) + checksum +=*(((uint8_t *)table)+i); + if (checksum) { + free (table); + table = NULL; + } + break; + } + offset += 16; + } + munmap(mem, 64*1024); +out: + close(fd); + return table; +} + +void pirq_free_table(struct routing_table *table) +{ + free(table); +} + + +#ifdef UNIT_TEST_PIRQ + +static void +pirq_unparse_slot(struct slot_entry *slot) +{ + printf("Slot %d: PCI %x:%x. ", + slot->slot, slot->bus, PCI_DEVICE(slot->device)); + printf("INTA link %x irq %x ", slot->inta_link, slot->inta_irq); + printf("B link %x irq %x ", slot->intb_link, slot->intb_irq); + printf("C link %x irq %x ", slot->intc_link, slot->intc_irq); + printf("D link %x irq %x ", slot->intd_link, slot->intd_irq); + printf("\n"); +} + +static void +pirq_unparse_routing_table(struct routing_table *table) +{ + int i, num_slots; + struct slot_entry *slot; + char buf[5]; buf[4] = 0; + memcpy(buf, &table->signature, 4); + + printf("PCI IRQ Routing Table\n"); + printf("Signature: %s\n", buf); + printf("Version : %x\n", table->version); + printf("Size : %xh\n", table->size); + printf("Bus : %x\n", table->router_bus); + printf("DevFn : %x\n", table->router_devfn); + printf("Exclusive IRQs : %x\n", table->exclusive_irqs); + printf("Compatable Router: %x\n", table->compatable_router); + + num_slots = (table->size - 32) / sizeof(*slot); + slot = &table->slot[0]; + for (i=0; i<num_slots; i++) { + pirq_unparse_slot(&slot[i]); + } +} + +int main(int argc, char *argv[]) +{ + struct routing_table *table; + table = pirq_alloc_read_table(); + if (!table) + return 1; + pirq_unparse_routing_table(table); + pirq_free_table(table); + return 0; +} + +#endif diff --git a/src/pirq.h b/src/pirq.h new file mode 100644 index 0000000..293b81d --- /dev/null +++ b/src/pirq.h @@ -0,0 +1,58 @@ +#ifndef PIRQ_H_INCLUDED +#define PIRQ_H_INCLUDED + +/* + * PCI IRQ Routing Table dumper + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * + * Based on the description of said table found at + * http://www.microsoft.com/hwdev/busbios/pciirq.htm + * Licensed under the GNU General Public license, version 2. + */ + +#include <stdint.h> +#include <stdlib.h> + +typedef unsigned char __u8; +typedef unsigned short int __u16; +typedef unsigned int __u32; + + +struct slot_entry { + __u8 bus; + __u8 device; + __u8 inta_link; + __u16 inta_irq; + __u8 intb_link; + __u16 intb_irq; + __u8 intc_link; + __u16 intc_irq; + __u8 intd_link; + __u16 intd_irq; + __u8 slot; + __u8 reserved; +} __attribute__((packed)); + +struct routing_table { + __u32 signature; + __u16 version; + __u16 size; + __u8 router_bus; + __u8 router_devfn; + __u16 exclusive_irqs; + __u32 compatable_router; + __u32 miniport_data; + __u8 reserved[11]; + __u8 checksum; + struct slot_entry slot[1]; +} __attribute__((packed)); + + +#define PCI_DEVICE(devfn) (((devfn) >> 3) & 0x1f) +#define PCI_FUNC(devfn) ((devfn) & 0x07) +extern struct routing_table * pirq_alloc_read_table(void); +extern void pirq_free_table(struct routing_table *table); +extern int pirq_pci_dev_to_slot(struct routing_table *table, int bus, int dev); + +#endif /* PIRQ_H_INCLUDED */ diff --git a/src/read-cis.c b/src/read-cis.c new file mode 100644 index 0000000..9ee71e2 --- /dev/null +++ b/src/read-cis.c @@ -0,0 +1,275 @@ +/* + * read-cis.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The initial developer of the original code is David A. Hinds + * <dahinds@users.sourceforge.net>. Portions created by David A. Hinds + * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + * + * (C) 1999 David A. Hinds + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <syslog.h> +#include <linux/limits.h> + +#include "cistpl.h" + +#define MAX_TUPLES 0x200 + +#define PATH_TO_SOCKET "/sys/class/pcmcia_socket/" + +/* Bits in attr field */ +#define IS_ATTR 1 +#define IS_INDIRECT 8 + + +static unsigned int functions; +static unsigned char cis_copy[MAX_TUPLES]; +static unsigned int cis_length = MAX_TUPLES; + + +#define SPACE(f) (((tuple_flags *)(&(f)))->space) +#define HAS_LINK(f) (((tuple_flags *)(&(f)))->has_link) +#define LINK_SPACE(f) (((tuple_flags *)(&(f)))->link_space) +#define MFC_FN(f) (((tuple_flags *)(&(f)))->mfc_fn) + + +static void read_cis(int attr, unsigned int addr, unsigned int len, void *ptr) +{ + if (cis_length > addr+len) + memcpy(ptr, cis_copy+addr, len); + else + memset(ptr, 0xff, len); + return; +} + +int pcmcia_get_next_tuple(unsigned int function, tuple_t *tuple); + +int pcmcia_get_first_tuple(unsigned int function, tuple_t *tuple) +{ + tuple->TupleLink = tuple->Flags = 0; + { + /* Assume presence of a LONGLINK_C to address 0 */ + tuple->CISOffset = tuple->LinkOffset = 0; + SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1; + } + if ((functions > 1) && + !(tuple->Attributes & TUPLE_RETURN_COMMON)) { + unsigned char req = tuple->DesiredTuple; + tuple->DesiredTuple = CISTPL_LONGLINK_MFC; + if (!pcmcia_get_next_tuple(function, tuple)) { + tuple->DesiredTuple = CISTPL_LINKTARGET; + if (pcmcia_get_next_tuple(function, tuple)) + return -ENODEV; + } else + tuple->CISOffset = tuple->TupleLink = 0; + tuple->DesiredTuple = req; + } + return pcmcia_get_next_tuple(function, tuple); +} + + +static int follow_link(tuple_t *tuple) +{ + char link[5]; + unsigned int ofs; + + if (MFC_FN(tuple->Flags)) { + /* Get indirect link from the MFC tuple */ + read_cis(LINK_SPACE(tuple->Flags), + tuple->LinkOffset, 5, link); + ofs = *(u_int *)(link+1); + SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR); + /* Move to the next indirect link */ + tuple->LinkOffset += 5; + MFC_FN(tuple->Flags)--; + } else if (HAS_LINK(tuple->Flags)) { + ofs = tuple->LinkOffset; + SPACE(tuple->Flags) = LINK_SPACE(tuple->Flags); + HAS_LINK(tuple->Flags) = 0; + } else { + return -1; + } + if (SPACE(tuple->Flags)) { + /* This is ugly, but a common CIS error is to code the long + link offset incorrectly, so we check the right spot... */ + read_cis(SPACE(tuple->Flags), ofs, 5, link); + if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && + (strncmp(link+2, "CIS", 3) == 0)) + return ofs; + /* Then, we try the wrong spot... */ + ofs = ofs >> 1; + } + read_cis(SPACE(tuple->Flags), ofs, 5, link); + if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) && + (strncmp(link+2, "CIS", 3) == 0)) + return ofs; + return -1; +} + +int pcmcia_get_next_tuple(unsigned int function, tuple_t *tuple) +{ + unsigned char link[2], tmp; + int ofs, i, attr; + + link[1] = tuple->TupleLink; + ofs = tuple->CISOffset + tuple->TupleLink; + attr = SPACE(tuple->Flags); + + for (i = 0; i < MAX_TUPLES; i++) { + if (link[1] == 0xff) { + link[0] = CISTPL_END; + } else { + read_cis(attr, ofs, 2, link); + if (link[0] == CISTPL_NULL) { + ofs++; continue; + } + } + + /* End of chain? Follow long link if possible */ + if (link[0] == CISTPL_END) { + if ((ofs = follow_link(tuple)) < 0) + return -ENODEV; + attr = SPACE(tuple->Flags); + read_cis(attr, ofs, 2, link); + } + + /* Is this a link tuple? Make a note of it */ + if ((link[0] == CISTPL_LONGLINK_A) || + (link[0] == CISTPL_LONGLINK_C) || + (link[0] == CISTPL_LONGLINK_MFC) || + (link[0] == CISTPL_LINKTARGET) || + (link[0] == CISTPL_INDIRECT) || + (link[0] == CISTPL_NO_LINK)) { + switch (link[0]) { + case CISTPL_LONGLINK_A: + HAS_LINK(tuple->Flags) = 1; + LINK_SPACE(tuple->Flags) = attr | IS_ATTR; + read_cis(attr, ofs+2, 4, &tuple->LinkOffset); + break; + case CISTPL_LONGLINK_C: + HAS_LINK(tuple->Flags) = 1; + LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR; + read_cis(attr, ofs+2, 4, &tuple->LinkOffset); + break; + case CISTPL_INDIRECT: + HAS_LINK(tuple->Flags) = 1; + LINK_SPACE(tuple->Flags) = IS_ATTR | IS_INDIRECT; + tuple->LinkOffset = 0; + break; + case CISTPL_LONGLINK_MFC: + tuple->LinkOffset = ofs + 3; + LINK_SPACE(tuple->Flags) = attr; + if (function == BIND_FN_ALL) { + /* Follow all the MFC links */ + read_cis(attr, ofs+2, 1, &tmp); + MFC_FN(tuple->Flags) = tmp; + } else { + /* Follow exactly one of the links */ + MFC_FN(tuple->Flags) = 1; + tuple->LinkOffset += function * 5; + } + break; + case CISTPL_NO_LINK: + HAS_LINK(tuple->Flags) = 0; + break; + } + if ((tuple->Attributes & TUPLE_RETURN_LINK) && + (tuple->DesiredTuple == RETURN_FIRST_TUPLE)) + break; + } else + if (tuple->DesiredTuple == RETURN_FIRST_TUPLE) + break; + + if (link[0] == tuple->DesiredTuple) + break; + ofs += link[1] + 2; + } + if (i == MAX_TUPLES) + return -ENODEV; + + tuple->TupleCode = link[0]; + tuple->TupleLink = link[1]; + tuple->CISOffset = ofs + 2; + + return 0; +} + +#define _MIN(a, b) (((a) < (b)) ? (a) : (b)) + +int pcmcia_get_tuple_data(tuple_t *tuple) +{ + unsigned int len; + + if (tuple->TupleLink < tuple->TupleOffset) + return -ENODEV; + len = tuple->TupleLink - tuple->TupleOffset; + tuple->TupleDataLen = tuple->TupleLink; + if (len == 0) + return 0; + + read_cis (SPACE(tuple->Flags), + tuple->CISOffset + tuple->TupleOffset, + _MIN(len, tuple->TupleDataMax), + tuple->TupleData); + + return 0; +} + + +int read_out_cis (unsigned int socket_no, FILE *fd) +{ + char file[PATH_MAX]; + int ret, i; + tuple_t tuple; + unsigned char buf[256]; + + snprintf(file, sizeof(file), PATH_TO_SOCKET "pcmcia_socket%d/cis", + socket_no); + + if (!fd) { + fd = fopen(file, "r"); + if (!fd) + return -EIO; + } + + for (i=0; i<MAX_TUPLES; i++) { + ret = fgetc(fd); + if (ret == EOF) { + cis_length = i + 1; + break; + } + cis_copy[i] = (unsigned char) ret; + } + fclose(fd); + + if (cis_length < 4) + return -EINVAL; + + functions = 1; + + tuple.DesiredTuple = CISTPL_LONGLINK_MFC; + tuple.Attributes = TUPLE_RETURN_COMMON; + + ret = pcmcia_get_first_tuple(BIND_FN_ALL, &tuple); + if (ret) + functions = 1; + + tuple.TupleData = buf; + tuple.TupleOffset = 0; + tuple.TupleDataMax = 255; + ret = pcmcia_get_tuple_data(&tuple); + if (ret) + return -EBADF; + + functions = tuple.TupleData[0]; + + return 0; +} diff --git a/src/read_proc.c b/src/read_proc.c new file mode 100644 index 0000000..aea203f --- /dev/null +++ b/src/read_proc.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + * Copied from from net-tools-1.60, also under the GNU GPL v2, + * and modified for use here. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <errno.h> +#include "eths.h" + +#define _PATH_PROCNET_DEV "/proc/net/dev" + +static struct network_device *add_interface(struct libbiosdevname_state *state, + const char *name) +{ + struct network_device *i; + i = malloc(sizeof(*i)); + if (!i) + return NULL; + memset(i, 0, sizeof(*i)); + INIT_LIST_HEAD(&i->node); + snprintf(i->kernel_name, sizeof(i->kernel_name), name); + list_add_tail(&i->node, &state->network_devices); + return i; +} + + +static char *get_name(char **namep, char *p) +{ + int count = 0; + char *name; + while (isspace(*p)) + p++; + name = *namep = p; + while (*p) { + if (isspace(*p)) + break; + if (*p == ':') { /* could be an alias */ + char *dot = p, *dotname = name; + *name++ = *p++; + count++; + while (isdigit(*p)){ + *name++ = *p++; + count++; + if (count == (IFNAMSIZ-1)) + break; + } + if (*p != ':') { /* it wasn't, backup */ + p = dot; + name = dotname; + } + if (*p == '\0') + return NULL; + p++; + break; + } + *name++ = *p++; + count++; + if (count == (IFNAMSIZ-1)) + break; + } + *name++ = '\0'; + return p; +} + + + +int get_interfaces(struct libbiosdevname_state *state) +{ + FILE *fh; + int err; + char *line = NULL; + size_t linelen = 0; + + fh = fopen(_PATH_PROCNET_DEV, "r"); + if (!fh) { + fprintf(stderr, "Error: cannot open %s (%s).\n", + _PATH_PROCNET_DEV, strerror(errno)); + return 1; + } + if (getline(&line, &linelen, fh) == -1 /* eat line */ + || getline(&line, &linelen, fh) == -1) { + err = -1; + goto out; + } + + err = 0; + while (getline(&line, &linelen, fh) != -1) { + char *s, *name; + s = get_name(&name, line); + add_interface(state, name); + } + if (ferror(fh)) { + perror(_PATH_PROCNET_DEV); + err = -1; + } + +out: + free(line); + fclose(fh); + return err; +} diff --git a/src/state.h b/src/state.h new file mode 100644 index 0000000..25bcc61 --- /dev/null +++ b/src/state.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2006 Dell, Inc. + * by Matt Domsch <Matt_Domsch@dell.com> + * Licensed under the GNU General Public license, version 2. + */ +#ifndef LIBBIOSDEVICE_STATE_H_INCLUDED +#define LIBBIOSDEVICE_STATE_H_INCLUDED + +#include "list.h" + +struct libbiosdevname_state { + struct list_head bios_devices; + struct list_head pci_devices; + struct list_head pcmcia_devices; + struct list_head network_devices; +}; + +#endif /* LIBBIOSDEVICESTATE_H_INCLUDED */ |