/*
 * Hardware Information, version 0.3
 * Copyright (C) 2003 Leandro Pereira <leandro@linuxmag.com.br>
 *
 * May be modified and/or distributed under the terms of GNU GPL version 2.
 *
 */

#include "hardinfo.h"
#include "pci.h"

void hi_show_pci_info(MainWindow *mainwindow, PCIDevice *device)
{
	gchar *buf;

	if(!device) return;

	gtk_window_set_title(GTK_WINDOW(mainwindow->det_window->window), device->category);
	detail_window_set_icon(mainwindow->det_window, IMG_PREFIX "pci.png");
 	detail_window_set_dev_name(mainwindow->det_window, device->name);
 	detail_window_set_dev_type(mainwindow->det_window, device->category);

	if (device->irq)
		detail_window_append_info_int(mainwindow->det_window, "IRQ",
					      device->irq, FALSE);
	
	if(device->io_addr) {
		buf = g_strdup_printf(_("0x%x to 0x%x"), device->io_addr,
			device->io_addr_end);
	
		detail_window_append_info(mainwindow->det_window,
					  _("I/O Address"), buf);	
		g_free(buf);				
	}

	if(device->memory) {
		buf = g_strdup_printf(_("%d%s"),
			(device->memory <= 1024) ? device->memory :
				device->memory / 1000,
			(device->memory <= 1024) ? "bytes" : "KB");
	
		detail_window_append_info(mainwindow->det_window,
					  _("Memory"), buf);
	
		g_free(buf);				
	}

	if(device->freq) {
		buf = g_strdup_printf(_("%dMHz"), device->freq);

		detail_window_append_info(mainwindow->det_window,
					  _("Frequency"), buf);
	
		g_free(buf);				
	}

	if (device->latency)
		detail_window_append_info_int(mainwindow->det_window,
					      _("Latency"), device->latency,
					      FALSE);

	detail_window_append_info(mainwindow->det_window, _("Bus master"), 
				  (gchar*)((device->bus_master) ? _("Yes") : _("No")));

	detail_window_append_separator(mainwindow->det_window);
	
	detail_window_append_info_int(mainwindow->det_window, _("Domain"),
				      device->domain, FALSE);
	detail_window_append_info_int(mainwindow->det_window, _("Bus"),
				      device->bus, FALSE);
	detail_window_append_info_int(mainwindow->det_window, _("Device"),
				      device->device, FALSE);
	detail_window_append_info_int(mainwindow->det_window, _("Function"),
				      device->function, FALSE);
}

#ifdef USE_LSPCI
PCIDevice *hi_scan_pci(void)
{
	FILE *lspci;
	gchar buffer[256], *buf;
	gint n=0;
	PCIDevice *pci_dev = NULL, *pci;
	
	pci = NULL;
	
	lspci = popen(LSPCI, "r");
	if(!lspci) return NULL;
	
	while(fgets(buffer, 256, lspci)){
		buf = g_strstrip(buffer);
		
		if(!strncmp(buf, "Flags", 5)){
			gint irq=0, freq=0, latency=0, i;
			gchar **list;
		
			buf+=7;
			
			pci_dev->bus_master = FALSE;
			
			list = g_strsplit(buf, ", ", 10);
			for (i = 0; i <= 10; i++) {
				if(!list[i]) break;
			
				if(!strncmp(list[i], "IRQ", 3))
					sscanf(list[i], "IRQ %d", &irq);
				else if(strstr(list[i], "Mhz"))
					sscanf(list[i], "%dMhz", &freq);
				else if(!strncmp(list[i], "bus master", 10))
					pci_dev->bus_master = TRUE; 
				else if(!strncmp(list[i], "latency", 7))
					sscanf(list[i], "latency %d", &latency);
			}
			g_strfreev(list);
			
			if (irq)     pci_dev->irq = irq;
			if (freq)    pci_dev->freq = freq;
			if (latency) pci_dev->latency = latency;
		} else if(!strncmp(buf, "Memory at", 9) &&
			  strstr(buf, "[size=")) {
			gulong mem;
			gchar unit;
			
			walk_until('[');
			sscanf(buf, "[size=%ld%c", &mem, &unit);

			mem *= (unit == ']') ? 1 :
			       (unit == 'K') ? 1024 :
			       (unit == 'M') ? 1024 * 1000 :
			       (unit == 'G') ? 1024 * 1000 * 1000 : 1;

			pci_dev->memory += mem; 
			
		} else if(!strncmp(buf, "I/O", 3)){
			guint io_addr, io_size;
			
			sscanf(buf, "I/O ports at %x [size=%d]", &io_addr, &io_size);
			
			pci_dev->io_addr	= io_addr;			
			pci_dev->io_addr_end	= io_addr+io_size;
		
		} else if((buf[0] >= '0' && buf[0] <= '9') && buf[4] == ':'){
			gint bus, device, function, domain;
			gpointer start, end;
			
			pci_dev = g_new0(PCIDevice, 1);

			pci_dev->next = pci;			
			pci = pci_dev;
			
			sscanf(buf, "%x:%x:%x.%d", &domain, &bus, &device, &function);
			
			pci_dev->domain		= domain;
			pci_dev->bus		= bus;
			pci_dev->device		= device;
			pci_dev->function 	= function;

			walk_until(' ');

			start = buf;
			
			walk_until(':');
			end = buf+1;
			*buf=0;

			buf = start+1;			
			pci_dev->category = g_strdup(buf);
			
			buf = end;
			start = buf;
			walk_until('(');
			*buf = 0;
			buf = start+1;
				
			pci_dev->name = g_strdup(buf);

			n++;
		}
	}
	pclose(lspci);
	
	return pci;
}

#else

#warning Using /proc/pci is deprecated, please install pciutils!

PCIDevice *hi_scan_pci(void)
{
	FILE *proc_pci;
	gchar buffer[256], *buf;
	gboolean next_is_name = FALSE;
	gint n=0;
	PCIDevice *pci_dev, *pci;
	struct stat st;
	
	g_print("Scanning PCI devices... ");
	
	pci = NULL;
	
	if(stat("/proc/pci", &st)) return NULL;
	
	proc_pci = fopen("/proc/pci", "r");
	while(fgets(buffer, 256, proc_pci)){
		buf = g_strstrip(buffer);
		if(next_is_name == TRUE) {
			gpointer start, end;
			
			start = buf;
			
			walk_until(':');
			end = buf+1;
			*buf=0;

			buf = start;			
			pci_dev->category = g_strdup(buf);
			
			buf = end;			
			buf[strlen(buf)-1]=0;
			pci_dev->name = g_strdup(buf);
			
			next_is_name = FALSE;
		} else if(!strncmp(buf, "IRQ", 3)){
			gint irq;
			
			sscanf(buf, "IRQ %d", &irq);
			
			pci_dev->irq		= irq;
		} else if(!strncmp(buf, "Prefetchable 32 bit", 19) ||
				!strncmp(buf, "Non-prefetchable 32 bit", 23)){
			gulong mem_st, mem_end;
			
			if(*buf == 'N') buf+=4;
			buf++;
			
			sscanf(buf, "refetchable 32 bit memory at %lx [%lx]",
				&mem_st, &mem_end);
			
			pci_dev->memory+=(mem_end-mem_st)/1024;
		} else if(!strncmp(buf, "I/O", 3)){
			guint io_addr, io_addr_end;
			
			sscanf(buf, "I/O at %x [%x]", &io_addr, &io_addr_end);
			
			pci_dev->io_addr	= io_addr;			
			pci_dev->io_addr_end	= io_addr_end;
		} else if(!strncmp(buf, "Bus", 3)){
			gint bus, device, function;
			
			pci_dev = g_new0(PCIDevice, 1);

			pci_dev->next = pci;			
			pci = pci_dev;
			
			sscanf(buf, "Bus %d, device %d, function %d:",
					&bus, &device, &function);
			
			pci_dev->bus		= bus;
			pci_dev->device		= device;
			pci_dev->function 	= function;

			next_is_name = TRUE;
			n++;
		}
	}
	fclose(proc_pci);
	
	g_print ("%d devices found\n", n);	
	
	return pci;
}
#endif