| /* ----------------------------------------------------------------------- * | 
 |  * | 
 |  *   Copyright 2005-2008 H. Peter Anvin - All Rights Reserved | 
 |  * | 
 |  *   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, Inc., 53 Temple Place Ste 330, | 
 |  *   Boston MA 02111-1307, USA; either version 2 of the License, or | 
 |  *   (at your option) any later version; incorporated herein by reference. | 
 |  * | 
 |  * ----------------------------------------------------------------------- */ | 
 |  | 
 | /* | 
 |  * ethersel.c | 
 |  * | 
 |  * Search for an Ethernet card with a known PCI signature, and run | 
 |  * the corresponding Ethernet module. | 
 |  * | 
 |  * To use this, set up a syslinux config file like this: | 
 |  * | 
 |  * PROMPT 0 | 
 |  * DEFAULT ethersel.c32 | 
 |  * # DEV [DID xxxx:yyyy[/mask]] [RID zz-zz] [SID uuuu:vvvv[/mask]] commandline | 
 |  * # ... | 
 |  * | 
 |  * DID = PCI device ID | 
 |  * RID = Revision ID (range) | 
 |  * SID = Subsystem ID | 
 |  */ | 
 |  | 
 | #include <inttypes.h> | 
 | #include <stdio.h> | 
 | #include <ctype.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <console.h> | 
 | #include <sys/pci.h> | 
 | #include <com32.h> | 
 | #include <syslinux/boot.h> | 
 | #include <syslinux/config.h> | 
 | #include <dprintf.h> | 
 |  | 
 | #define MAX_LINE 512 | 
 |  | 
 | /* Check to see if we are at a certain keyword (case insensitive) */ | 
 | static int looking_at(const char *line, const char *kwd) | 
 | { | 
 |     const char *p = line; | 
 |     const char *q = kwd; | 
 |  | 
 |     while (*p && *q && ((*p ^ *q) & ~0x20) == 0) { | 
 | 	p++; | 
 | 	q++; | 
 |     } | 
 |  | 
 |     if (*q) | 
 | 	return 0;		/* Didn't see the keyword */ | 
 |  | 
 |     return *p <= ' ';		/* Must be EOL or whitespace */ | 
 | } | 
 |  | 
 | static char *get_did(char *p, uint32_t * idptr, uint32_t * maskptr) | 
 | { | 
 |     unsigned long vid, did, m1, m2; | 
 |  | 
 |     *idptr = -1; | 
 |     *maskptr = 0xffffffff; | 
 |  | 
 |     vid = strtoul(p, &p, 16); | 
 |     if (*p != ':') | 
 | 	return p;		/* Bogus ID */ | 
 |     did = strtoul(p + 1, &p, 16); | 
 |  | 
 |     *idptr = (did << 16) + vid; | 
 |  | 
 |     if (*p == '/') { | 
 | 	m1 = strtoul(p + 1, &p, 16); | 
 | 	if (*p != ':') { | 
 | 	    *maskptr = (m1 << 16) | 0xffff; | 
 | 	} else { | 
 | 	    m2 = strtoul(p + 1, &p, 16); | 
 | 	    *maskptr = (m1 << 16) | m2; | 
 | 	} | 
 |     } | 
 |  | 
 |     return p; | 
 | } | 
 |  | 
 | static char *get_rid_range(char *p, uint8_t * rid_min, uint8_t * rid_max) | 
 | { | 
 |     unsigned long r0, r1; | 
 |  | 
 |     p = skipspace(p + 3); | 
 |  | 
 |     r0 = strtoul(p, &p, 16); | 
 |     if (*p == '-') { | 
 | 	r1 = strtoul(p + 1, &p, 16); | 
 |     } else { | 
 | 	r1 = r0; | 
 |     } | 
 |  | 
 |     *rid_min = r0; | 
 |     *rid_max = r1; | 
 |  | 
 |     return p; | 
 | } | 
 |  | 
 | static struct match *parse_config(const char *filename) | 
 | { | 
 |     char line[MAX_LINE], *p; | 
 |     FILE *f; | 
 |     struct match *list = NULL; | 
 |     struct match **ep = &list; | 
 |     struct match *m; | 
 |  | 
 |     if (!filename) | 
 | 	filename = syslinux_config_file(); | 
 |  | 
 |     f = fopen(filename, "r"); | 
 |     if (!f) | 
 | 	return list; | 
 |  | 
 |     while (fgets(line, sizeof line, f)) { | 
 | 	p = skipspace(line); | 
 |  | 
 | 	if (!looking_at(p, "#")) | 
 | 	    continue; | 
 | 	p = skipspace(p + 1); | 
 |  | 
 | 	if (!looking_at(p, "dev")) | 
 | 	    continue; | 
 | 	p = skipspace(p + 3); | 
 |  | 
 | 	m = malloc(sizeof(struct match)); | 
 | 	if (!m) | 
 | 	    continue; | 
 |  | 
 | 	memset(m, 0, sizeof *m); | 
 | 	m->rid_max = 0xff; | 
 |  | 
 | 	for (;;) { | 
 | 	    p = skipspace(p); | 
 |  | 
 | 	    if (looking_at(p, "did")) { | 
 | 		p = get_did(p + 3, &m->did, &m->did_mask); | 
 | 	    } else if (looking_at(p, "sid")) { | 
 | 		p = get_did(p + 3, &m->sid, &m->sid_mask); | 
 | 	    } else if (looking_at(p, "rid")) { | 
 | 		p = get_rid_range(p + 3, &m->rid_min, &m->rid_max); | 
 | 	    } else { | 
 | 		char *e; | 
 |  | 
 | 		e = strchr(p, '\n'); | 
 | 		if (*e) | 
 | 		    *e = '\0'; | 
 | 		e = strchr(p, '\r'); | 
 | 		if (*e) | 
 | 		    *e = '\0'; | 
 |  | 
 | 		m->filename = strdup(p); | 
 | 		if (!m->filename) | 
 | 		    m->did = -1; | 
 | 		break;		/* Done with this line */ | 
 | 	    } | 
 | 	} | 
 |  | 
 | 	dprintf("DEV DID %08x/%08x SID %08x/%08x RID %02x-%02x CMD %s\n", | 
 | 		m->did, m->did_mask, m->sid, m->sid_mask, | 
 | 		m->rid_min, m->rid_max, m->filename); | 
 |  | 
 | 	*ep = m; | 
 | 	ep = &m->next; | 
 |     } | 
 |  | 
 |     return list; | 
 | } | 
 |  | 
 | int main(int argc, char *argv[]) | 
 | { | 
 |     struct match *list, *match; | 
 |     struct pci_domain *pci_domain; | 
 |  | 
 |     pci_domain = pci_scan(); | 
 |  | 
 |     if (pci_domain) { | 
 | 	list = parse_config(argc < 2 ? NULL : argv[1]); | 
 |  | 
 | 	match = find_pci_device(pci_domain, list); | 
 |  | 
 | 	if (match) | 
 | 	    syslinux_run_command(match->filename); | 
 |     } | 
 |  | 
 |     /* On error, return to the command line */ | 
 |     fputs("Error: no recognized network card found!\n", stderr); | 
 |     return 1; | 
 | } |