/* Copyright 1999, 2000 Red Hat, Inc.
 *
 * This software may be freely redistributed under the terms of the GNU
 * public license.
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "kudzu.h"
#include "ddc.h"
#include "ide.h"
#include "isapnp.h"
#include "misc.h"
#include "modules.h"
#include "parallel.h"
#include "pci.h"
#include "psaux.h"
#include "usb.h"
#include "serial.h"
#include "sbus.h"
#include "scsi.h"
#include "keyboard.h"

#include <fcntl.h>
#include <fnmatch.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "device.h"

#ifndef __LOADER__

static struct {
    char *prefix;
    char *match;
} fbcon_drivers[] = {
/* The first string is a prefix of fix->id reported by fbcon
   (check linux/drivers/video for that), the latter is
   a shell pattern (see glob(7)) of either the desc or driver
   strings probed by kudzu library (for PCI you can find this
   in pcitable file). */
{ "ATY Mach64", "*:*Mach64*" },
{ "BWtwo", "Sun|Monochrome (bwtwo)" },
{ "CGfourteen", "Sun|SX*" },
{ "CGsix ", "Sun|*GX*" },
{ "CGthree", "Sun|Color3 (cgthree)" },
{ "CLgen", "Cirrus Logic|GD *" },
{ "Creator", "Sun|FFB*" },
{ "DEC 21030 TGA", "DEC|DECchip 21030 [TGA]" },
{ "Elite 3D", "Sun|Elite3D*" },
{ "Leo", "Sun|*ZX*" },
{ "MATROX", "Matrox|MGA*" },
{ "Permedia2", "*3DLabs*" },
{ "TCX", "Sun|TCX*" },
{ "VESA VGA", "FBDev*" },
{ "VGA16 VGA", "FBDev*" },
{ NULL, NULL },
};

#endif

char *classStrings[] = {
	"UNSPEC", "OTHER", "NETWORK", "SCSI", "MOUSE", "AUDIO",
	"CDROM", "MODEM", "VIDEO", "TAPE", "FLOPPY", "SCANNER",
	"HD", "RAID", "PRINTER", "CAPTURE", "KEYBOARD",
	  "MONITOR", "USB", "SOCKET", NULL
};

struct bus buses[] = {
	{ BUS_UNSPEC, "UNSPEC", NULL, NULL, NULL, NULL },
	{ BUS_OTHER, "OTHER", (newFunc *)newDevice, NULL, NULL, NULL },
	{ BUS_PCI, "PCI", (newFunc *)pciNewDevice, pciReadDrivers, pciFreeDrivers, pciProbe },
#if !defined(__LOADER__) || defined(__sparc__)
	{ BUS_SBUS, "SBUS", (newFunc *)sbusNewDevice, NULL, NULL, sbusProbe },
#endif
	{ BUS_USB, "USB", (newFunc *)usbNewDevice, usbReadDrivers, usbFreeDrivers, usbProbe },
#ifndef __LOADER__
	{ BUS_PSAUX, "PSAUX", (newFunc *)psauxNewDevice, NULL, NULL, psauxProbe },
	{ BUS_SERIAL, "SERIAL", (newFunc *)serialNewDevice, NULL, NULL, serialProbe },
	{ BUS_PARALLEL, "PARALLEL", (newFunc *)parallelNewDevice, parallelReadDrivers, parallelFreeDrivers, parallelProbe },
#endif /* LOADER */
	{ BUS_SCSI, "SCSI", (newFunc *)scsiNewDevice, NULL, NULL, scsiProbe },
	{ BUS_IDE, "IDE", (newFunc *)ideNewDevice, NULL, NULL, ideProbe },
#ifndef __LOADER__
	{ BUS_KEYBOARD, "KEYBOARD", (newFunc *)keyboardNewDevice, NULL, NULL, keyboardProbe },
	{ BUS_DDC, "DDC", (newFunc *)ddcNewDevice, ddcReadDrivers, ddcFreeDrivers, ddcProbe },
        { BUS_ISAPNP, "ISAPNP", (newFunc *)isapnpNewDevice, isapnpReadDrivers, isapnpFreeDrivers, isapnpProbe },
#endif /* LOADER */
	{ BUS_MISC, "MISC", (newFunc *)miscNewDevice, NULL, NULL, miscProbe },
	{ -1, NULL, NULL, NULL, NULL, NULL }
};

struct device *newDevice(struct device *old, struct device *new) {
    if (!old) {
	if (!new) {
	    new = malloc(sizeof(struct device));
	    memset(new,'\0',sizeof(struct device));
	}
	    new->class = CLASS_UNSPEC;
    } else {
	    new->class = old->class;
	    if (old->device) new->device = strdup(old->device);
	    if (old->driver) new->driver = strdup(old->driver);
	    if (old->desc) new->desc = strdup(old->desc);
	    new->detached = old->detached;
    }
    new->newDevice = newDevice;
    new->freeDevice = freeDevice;
	new->compareDevice = compareDevice;
    return new;
}

void freeDevice(struct device *dev) {
    if (!dev) {
	    printf("freeDevice(null)\n");
	    abort(); /* return; */
    }
    if (dev->device) free (dev->device);
    if (dev->driver) free (dev->driver);
    if (dev->desc) free (dev->desc);
    free (dev);
}

void writeDevice(FILE *file, struct device *dev) {
	int bus, i;

	if (!file) {
		printf("writeDevice(null,dev)\n");
		abort();
	}
	if (!dev) {
		printf("writeDevice(file,null)\n");
		abort();
	}
	bus = 0;
	for (i = 0; buses[i].busType != -1; i++)
	  if (dev->bus == buses[i].busType)
	    bus = i;
	fprintf(file,"-\nclass: %s\nbus: %s\ndetached: %d\n",
		classStrings[dev->class],buses[bus].string,dev->detached);
	if (dev->device) 
	  fprintf(file,"device: %s\n",dev->device);
	fprintf(file,"driver: %s\ndesc: \"%s\"\n",dev->driver,dev->desc);
}

int compareDevice(struct device *dev1, struct device *dev2) {
	if (!dev1 || !dev2) return 1;
	if (dev1->class != dev2->class) return 1;
	if (dev1->bus != dev2->bus) return 1;
	if (dev1->device && dev2->device && strcmp(dev1->device,dev2->device))
	  return 1;
	/* Look - a special case!
	 * If it's just the driver that changed, we might
	 * want to act differently on upgrades.
	 */
	if (strcmp(dev1->driver,dev2->driver)) return 2;
	return 0;
}

struct device *listRemove(struct device *devlist, struct device *dev) {
	struct device *head,*ptr,*prev;

	head = ptr = devlist;
	prev = NULL;
	while (ptr != NULL) {
		if (!ptr->compareDevice(ptr,dev)) {
			if (ptr == head) {
				head = head->next;
			} else {
				prev->next = ptr->next;
			}
			return head;
		}
		prev = ptr;
		ptr = ptr->next;
	}
	return head;
}

void sortNetDevices(struct device *devs) {
	struct device *cur, *next, *tmp;
	char *modulename;
	
	cur = devs;
	while (cur && cur->class != CLASS_NETWORK) {
		cur = cur->next;
	}
	while (cur && cur->class == CLASS_NETWORK) {
		modulename = cur->driver;
		next = cur->next;
		if (!next || next->class != CLASS_NETWORK) return;
		tmp = next->next;
		while (tmp && tmp->class == CLASS_NETWORK) {
			if (!strcmp(tmp->driver,modulename)) {
				next->next = tmp->next;
				tmp->next = cur->next;
				cur->next = tmp;
				cur = cur->next;
			}
			next = tmp;
			tmp = next->next;
		}
		if (cur) 
		  cur = cur->next;
	}
	return;
}

#ifndef __LOADER__

struct device *readDevice(FILE *file) {
	char *linebuf=malloc(512);
	struct device *retdev=NULL, *tmpdev;
	int i,x=0;

	if (!file) {
		printf("readDevice(null)\n");
		abort();
	}
	memset(linebuf,'\0',512);
	while (strcmp(linebuf,"-")) {
		memset(linebuf,'\0',512);
		linebuf=fgets(linebuf,512,file);
		if (!linebuf) break;
		/* kill trailing \n */
		(*rindex(linebuf,'\n'))='\0';
		if (!strcmp(linebuf,"-")) {
			break;
		} else {
			if (!retdev) retdev = newDevice(NULL,NULL);
		}
		if (!strncmp(linebuf,"class:",6)) {
			for (i=0; 
			     classStrings[i] && strcmp(classStrings[i],linebuf+7);
			     i++);
			if (classStrings[i])
			  retdev->class = i;
			else
			  retdev->class = CLASS_OTHER;
		} else if (!strncmp(linebuf,"bus:",4)) {
			for (i=0; 
			     buses[i].string && strcmp(buses[i].string,linebuf+5);
			     i++);
			if (buses[i].string) {
				tmpdev = (struct device *)buses[i].newFunc(retdev);
				retdev->freeDevice(retdev);
				retdev = tmpdev;
			} else
			  retdev->bus = BUS_OTHER;
		} else if (!strncmp(linebuf,"driver:",7)) {
			retdev->driver = strdup(linebuf+8);
		} else if (!strncmp(linebuf,"detached:",9)) {
			retdev->detached = atoi(linebuf+10);
		} else if (!strncmp(linebuf,"device:",7)) {
			retdev->device = strdup(linebuf+8);
		} else if (!strncmp(linebuf,"desc:",5)) {
			if (rindex(linebuf,'"')!=index(linebuf,'"')) {
				(*rindex(linebuf,'"')) = '\0';
				retdev->desc = strdup(index(linebuf,'"')+1);
			} else {
				retdev->desc = strdup(linebuf+6);
			}
		}
		switch (retdev->bus) {
		 case BUS_PCI:
			if (!strncmp(linebuf,"vendorId:",9))
			  ((struct pciDevice *)retdev)->vendorId = strtoul(linebuf+10, (char **) NULL, 16);
			else if (!strncmp(linebuf,"deviceId:",9))
			  ((struct pciDevice *)retdev)->deviceId = strtoul(linebuf+10, (char **) NULL, 16);
			else if (!strncmp(linebuf,"pciType:",8))
			  ((struct pciDevice *)retdev)->pciType = strtol(linebuf+9, (char **) NULL, 10);
			else if (!strncmp(linebuf,"subVendorId:",12))
			  ((struct pciDevice *)retdev)->subVendorId = strtoul(linebuf+13, (char **) NULL, 16);
			else if (!strncmp(linebuf,"subDeviceId:",12))
			  ((struct pciDevice *)retdev)->subDeviceId = strtoul(linebuf+13, (char **) NULL, 16);
			break;
		 case BUS_PARALLEL:
			if (!strncmp(linebuf,"pnpmodel:",9))
			  ((struct parallelDevice *)retdev)->pnpmodel = strdup(linebuf+10);
			if (!strncmp(linebuf,"pnpmfr:",7))
			  ((struct parallelDevice *)retdev)->pnpmfr = strdup(linebuf+8);
			if (!strncmp(linebuf,"pnpmodes:",9))
			  ((struct parallelDevice *)retdev)->pnpmodes = strdup(linebuf+10);
			if (!strncmp(linebuf,"pnpdesc:",8))
			  ((struct parallelDevice *)retdev)->pnpdesc = strdup(linebuf+9);
			if (!strncmp(linebuf,"pinfo.xres:",11))
			  ((struct parallelDevice *)retdev)->pinfo->xres = atoi(linebuf+12);
			if (!strncmp(linebuf,"pinfo.yres:",11))
			  ((struct parallelDevice *)retdev)->pinfo->yres = atoi(linebuf+12);
			if (!strncmp(linebuf,"pinfo.color:",12))
			  ((struct parallelDevice *)retdev)->pinfo->color = atoi(linebuf+13);
			if (!strncmp(linebuf,"pinfo.ascii:",12))
			  ((struct parallelDevice *)retdev)->pinfo->ascii = atoi(linebuf+13);
			if (!strncmp(linebuf,"pinfo.uniprint:",15))
			  ((struct parallelDevice *)retdev)->pinfo->uniprint = strdup(linebuf+16);
			break;
		 case BUS_SERIAL:
			if (!strncmp(linebuf,"pnpmodel:",9))
			  ((struct serialDevice *)retdev)->pnpmodel = strdup(linebuf+10);
			if (!strncmp(linebuf,"pnpmfr:",7))
			  ((struct serialDevice *)retdev)->pnpmfr = strdup(linebuf+8);
			if (!strncmp(linebuf,"pnpcompat:",10))
			  ((struct serialDevice *)retdev)->pnpcompat = strdup(linebuf+11);
			if (!strncmp(linebuf,"pnpdesc:",8))
			  ((struct serialDevice *)retdev)->pnpdesc = strdup(linebuf+9);
			break;
		 case BUS_SBUS:
			if (!strncmp(linebuf,"width:",6))
			  ((struct sbusDevice *)retdev)->width = atoi(linebuf+7);
			if (!strncmp(linebuf,"height:",7))
			  ((struct sbusDevice *)retdev)->height = atoi(linebuf+8);
			if (!strncmp(linebuf,"freq:",5))
			  ((struct sbusDevice *)retdev)->freq = atoi(linebuf+6);
			if (!strncmp(linebuf,"monitor:",8))
			  ((struct sbusDevice *)retdev)->monitor = atoi(linebuf+9);
			break;
		 case BUS_SCSI:
			if (!strncmp(linebuf,"host:",5))
			  ((struct scsiDevice *)retdev)->host = atoi(linebuf+6);
			if (!strncmp(linebuf,"channel:",8))
			  ((struct scsiDevice *)retdev)->channel = atoi(linebuf+9);
			if (!strncmp(linebuf,"id:",3))
			  ((struct scsiDevice *)retdev)->id = atoi(linebuf+4);
			if (!strncmp(linebuf,"lun:",3))
			  ((struct scsiDevice *)retdev)->lun = atoi(linebuf+4);
			break;
		 case BUS_IDE:
			if (!strncmp(linebuf,"physical:",9))
			  ((struct ideDevice *)retdev)->physical = strdup(linebuf+10);
			if (!strncmp(linebuf,"logical:",8))
			  ((struct ideDevice *)retdev)->logical = strdup(linebuf+9);
		        break;
		 case BUS_DDC:
			if (!strncmp(linebuf,"id:", 3))
			  ((struct ddcDevice *)retdev)->id = strdup(linebuf+4);
			if (!strncmp(linebuf,"horizSyncMin:",13))
			  ((struct ddcDevice *)retdev)->horizSyncMin = atoi(linebuf+14);
			if (!strncmp(linebuf,"horizSyncMax:",13))
			  ((struct ddcDevice *)retdev)->horizSyncMax = atoi(linebuf+14);
			if (!strncmp(linebuf,"vertRefreshMin:",15))
			  ((struct ddcDevice *)retdev)->vertRefreshMin = atoi(linebuf+16);
			if (!strncmp(linebuf,"vertRefreshMax:",15))
			  ((struct ddcDevice *)retdev)->vertRefreshMax = atoi(linebuf+16);
			if (!strncmp(linebuf,"mode:",5)) {
				struct ddcDevice *ddev = (struct ddcDevice*)retdev;
				char *tmp;

				ddev->modes = realloc(ddev->modes,(x+3)*sizeof(int));
				ddev->modes[x] = atoi(linebuf+6);
				tmp = strstr(linebuf,"x");
				ddev->modes[x+1] = atoi(tmp+1);
				ddev->modes[x+2] = 0;
				x+=2;
			}
			if (!strncmp(linebuf,"mem:",4))
			  ((struct ddcDevice *)retdev)->mem = atol(linebuf+5);
		        break;
		 case BUS_USB:
			if (!strncmp(linebuf,"usbclass:",9))
			  ((struct usbDevice *)retdev)->usbclass = atoi(linebuf+10);
			if (!strncmp(linebuf,"usbsubclass:",12))
			  ((struct usbDevice *)retdev)->usbsubclass = atoi(linebuf+13);
			if (!strncmp(linebuf,"usbprotocol:",12))
			  ((struct usbDevice *)retdev)->usbprotocol = atoi(linebuf+13);
			if (!strncmp(linebuf,"usbbus:",7))
			  ((struct usbDevice *)retdev)->usbbus = atoi(linebuf+8);
			if (!strncmp(linebuf,"usblevel:",9))
			  ((struct usbDevice *)retdev)->usblevel = atoi(linebuf+10);
			if (!strncmp(linebuf,"usbport:",8))
			  ((struct usbDevice *)retdev)->usbport = atoi(linebuf+9);
			if (!strncmp(linebuf,"vendorId:",9))
			  ((struct usbDevice *)retdev)->vendorId = strtol(linebuf+10,NULL,16);
			if (!strncmp(linebuf,"deviceId:",9))
			  ((struct usbDevice *)retdev)->deviceId = strtol(linebuf+10,NULL,16);
			if (!strncmp(linebuf,"productrevision:",16))
			  ((struct usbDevice *)retdev)->productrevision = strdup(linebuf+17);
			break;
		 case BUS_ISAPNP:
			if (!strncmp(linebuf,"deviceId:",9))
			  ((struct isapnpDevice *)retdev)->deviceId = strdup(linebuf+10);
			if (!strncmp(linebuf,"pdeviceId:",10))
			  ((struct isapnpDevice *)retdev)->pdeviceId = strdup(linebuf+11);
			if (!strncmp(linebuf,"native:", 7))
			  ((struct isapnpDevice *)retdev)->native = atoi(linebuf+8);
			if (!strncmp(linebuf,"active:", 7))
			  ((struct isapnpDevice *)retdev)->active = atoi(linebuf+8);
			if (!strncmp(linebuf,"cardnum:", 8))
			  ((struct isapnpDevice *)retdev)->cardnum = atoi(linebuf+9);
			if (!strncmp(linebuf,"logdev:", 7))
			  ((struct isapnpDevice *)retdev)->logdev = atoi(linebuf+8);
			if (!strncmp(linebuf,"compat:",7))
			  ((struct isapnpDevice *)retdev)->compat = strdup(linebuf+8);
			if (!strncmp(linebuf,"io:",3))
			  ((struct isapnpDevice *)retdev)->io = isapnpReadResources(linebuf+4,16);
			if (!strncmp(linebuf,"irq:",4))
			  ((struct isapnpDevice *)retdev)->irq = isapnpReadResources(linebuf+5,10);
			if (!strncmp(linebuf,"dma:",4))
			  ((struct isapnpDevice *)retdev)->dma = isapnpReadResources(linebuf+5,10);
			if (!strncmp(linebuf,"mem:",4))
			  ((struct isapnpDevice *)retdev)->mem = isapnpReadResources(linebuf+5,16);
			break;
		 default:
			break;
		}
	}
	return retdev;
}

#endif

int initializeDeviceList() {
	int bus;
	
	for (bus=0;buses[bus].string;bus++)
	  if (buses[bus].initFunc)
	    buses[bus].initFunc(NULL);
	return 0;
}

void freeDeviceList() {
	int bus;
	
	for (bus=0;buses[bus].string;bus++)
	  if (buses[bus].freeFunc)
	    buses[bus].freeFunc();
}

/* used to sort device lists by a) type, b) device, c) description */
static int devCmp( const void *a, const void *b )
{
        const struct device *one,*two;
	int x,y,z,zz;
	
	one=((const struct device **)a)[0];
	two=((const struct device **)b)[0];
	x=one->class - two->class;
	if (one->device && two->device)
	  y=strcmp(one->device,two->device);
	else {
		y = one->device - two->device;
	}
	z=two->index - one->index;
	zz=strcmp(one->desc,two->desc);
	if (x)
	  return x;
	else if (y)
	  return y;
	else if (z)
	  return z;
	else
	  return zz;
}

char *bufFromFd(int fd) {
	struct stat sbuf;
	char *buf;
	
	fstat(fd,&sbuf);
	buf = malloc(sbuf.st_size + 1);
	read(fd, buf, sbuf.st_size);
	buf[sbuf.st_size] = '\0';
	close(fd);
	return buf;
}

#ifndef __LOADER__

struct device ** readDevices ( char *fn ) {
	FILE *confFile;
	char *linebuf;
	struct device *dev, **retdevs=NULL;
	int num=0;
	int index=0,x;
	enum deviceClass cl=CLASS_UNSPEC;
	
	linebuf=calloc(512,sizeof(char));
	confFile = fopen(fn,"r");
	if (!confFile) return NULL;
	
	while (strcmp(linebuf,"-\n")) {
		linebuf=fgets(linebuf,512,confFile);
		if (!linebuf) return NULL;
	}
	while (1) {
		dev = readDevice(confFile);
		if (!dev) break;
		retdevs = realloc (retdevs,(num+2) * sizeof (struct device *));
		retdevs[num] = dev;
		retdevs[num+1] = NULL;
		num++;
	}
	fclose(confFile);
	qsort(retdevs, num, sizeof(struct device *), devCmp);
	for (x=0;retdevs[x];x++) {
		if (retdevs[x]->class!=cl) {
			index = 0;
		}
		retdevs[x]->index = index;
		cl = retdevs[x]->class;
		index++;
	}
	return retdevs;	
}

int writeDevices ( char *fn, struct device **devlist ) {
	int x;
	FILE *confFile;
	
	if (!devlist || !devlist[0]) return 1;
	confFile = fopen(fn,"w");
	if (!confFile) return 1;
	for (x=0;devlist[x];x++) {
		devlist[x]->writeDevice(confFile,devlist[x]);
	}
	fclose(confFile);
	return 0;
}

static void fbProbe( struct device *devices ) {
    FILE *procfb;
    int i, j;
    char name[4], buffer[50], *id, *end;
    struct device *d;

    procfb = fopen("/proc/fb","r");
    if (!procfb) return;
    while (fgets(buffer, 50, procfb) != NULL) {
    	i = atoi (buffer);
    	id = strchr (buffer, ' ') + 1;
	end = id + strlen(id) - 1;
	while (*end && (*end == '\n' || *end == ' '))
	  *(end--) = '\0';
	for (j = 0; fbcon_drivers[j].prefix; j++)
	    if (!strncmp (id, fbcon_drivers[j].prefix,
			  strlen (fbcon_drivers[j].prefix)))
		break;
	    if (!fbcon_drivers[j].prefix)
		continue;
	    for (d = devices; d; d = d->next) {
		if (!d->device && d->class == CLASS_VIDEO &&
		    ((!fnmatch (fbcon_drivers[j].match,
			       d->desc, FNM_NOESCAPE) ||
		     !fnmatch (fbcon_drivers[j].match,
			       d->driver, FNM_NOESCAPE)) ||
		 	!strcmp(fbcon_drivers[j].match, "FBDev*"))) {
		    sprintf(name, "fb%d", i);
		    d->device = strdup (name);
		}
	    }
    }
    fclose(procfb);
}

#endif

struct device ** probeDevices ( enum deviceClass probeClass,
			      enum deviceBus probeBus,
			      int probeFlags
			      ) {
	struct device *devices=NULL,**devlist=NULL;
	int numDevs=0, bus, x, index=0;
	enum deviceClass cl=CLASS_UNSPEC;
	int logLevel = -1;

#ifndef __LOADER__
	logLevel = getLogLevel();
	setLogLevel(1);
#endif	
	
	for (bus=1;buses[bus].string;bus++) {
		if ( probeBus & buses[bus].busType || (probeBus == BUS_UNSPEC && buses[bus].busType != BUS_DDC))
		  if (buses[bus].probeFunc)
		    devices = buses[bus].probeFunc(probeClass, probeFlags, devices);
		if ((probeFlags & PROBE_ONE) && (devices))
		  break;
	}
	if (devices == NULL) {
#ifndef __LOADER__
		setLogLevel(logLevel);
#endif
		return NULL;
	}
#ifndef __LOADER__		
	if (probeClass==CLASS_VIDEO || probeClass == CLASS_UNSPEC)
	    fbProbe(devices);
#endif
	while (devices) {
		devlist=realloc(devlist, (numDevs+2) * sizeof(struct device *));
		devlist[numDevs]=devices;
		devlist[numDevs+1]=NULL;
		numDevs++;
		devices=devices->next;
	}
	qsort(devlist, numDevs, sizeof(struct device *), devCmp);
	/* We need to sort the network devices by module name. Fun. */
	for (x=0; devlist[x]; x++) {
		devlist[x]->next = devlist[x+1];
	}
	sortNetDevices(devlist[0]);
	devices = devlist[0];
	for (x = 0; x < numDevs ; x++) {
		devlist[x] = devices;
		devices = devices->next;
	}

	for (x=0;devlist[x];x++) {
		if (devlist[x]->class!=cl) {
			index = 0;
		}
		devlist[x]->index = index;
		cl = devlist[x]->class;
		index++;
	}
#ifndef __LOADER__
	setLogLevel(logLevel);
#endif
	return devlist;
}

/* Stub these here; assume failure. */
#ifdef __LOADER__
int loadModule(char *module) {
	return 1;
}
int removeModule(char *module) {
	return 1;
}

struct confModules *readConfModules(char *filename)
{
    return NULL;
}

char *getAlias(struct confModules *cf, char *alias)
{
    return NULL;
}

void freeConfModules(struct confModules *cf)
{
}

void checkPCISerial(void *foo, void *bar)
{
}
#endif

#ifndef __LOADER__

int listCompare( struct device **list1, struct device **list2, 
		struct device ***retlist1,
		struct device ***retlist2) {
	struct device *curr1, *prev1, *curr2, *prev2, *head1, *head2;
	struct device **ret1=NULL, **ret2=NULL;
	int x, notfound=1;
	
	/* Turn arrays into lists. */
	for (x=0;list1[x];x++) {
		list1[x]->next = list1[x+1];
	}
	for (x=0;list2[x];x++) {
		list2[x]->next = list2[x+1];
	}
	curr1 = head1 = list1[0];
	head2 = list2[0];
	prev1 = NULL;
	while (curr1) {
		curr2 = head2;
		prev2 = NULL;
		while (curr2) {
			if (!(notfound=curr1->compareDevice(curr1,curr2))) {
				if (!prev1) 
				  head1 = curr1->next;
				else
				  prev1->next = curr1->next;
				if (!prev2) 
				  head2 = curr2->next;
				else 
				  prev2->next = curr2->next;
				break;
			} else {
				prev2 = curr2;
				curr2 = curr2->next;
			}
		}
		if (notfound)
		  prev1 = curr1;
		curr1 = curr1 ->next;
	}
	/* Generate return lists */
	if (retlist1) {
		curr1 = head1;
		ret1=malloc(sizeof(struct device *));
		ret1[0]=NULL;
		for(x=0;curr1;x++) {
			ret1=realloc(ret1,(x+2)*sizeof(struct device *));
			ret1[x]=curr1;
			curr1=curr1->next;
		}
		ret1[x]=NULL;
		(*retlist1)=ret1;
	}
	if (retlist2) {
		curr2 = head2;
		ret2=malloc(sizeof(struct device *));
		ret2[0]=NULL;
		for(x=0;curr2;x++) {
			ret2=realloc(ret2,(x+2)*sizeof(struct device *));
			ret2[x]=curr2;
			curr2=curr2->next;
		}
		ret2[x]=NULL;
		(*retlist2)=ret2;
	}
	if (head1 || head2 )
	  return 1;
	else
	  return 0;
}

#endif
