/*
  Copyright (c) 2000 Caldera Systems

  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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "pciinfo.h"

#if defined(HAVE_LINUX_PCI_H)
#include <linux/pci.h>
#endif

#include <stdlib.h>

#include <qfile.h>
#include <qtextstream.h>

#include <kstaticdeleter.h>
#include <klocale.h>
#include <kmessagebox.h>

PCIInfo::DeviceInfoMap *PCIInfo::s_procBusPCIDevicesList = 0;
KStaticDeleter<PCIInfo::DeviceInfoMap> pciinfosd;
bool PCIInfo::s_initialized = false;

bool PCIInfo::lookupVendorAndDeviceID( const QString &busIDString, uint &vendorID, uint &deviceID )
{
    if ( !initStatic() )
        return false;

    DeviceInfoMap::ConstIterator it = s_procBusPCIDevicesList->find( busIDString );

    if ( it == s_procBusPCIDevicesList->end() )
         return false;

    vendorID = (*it).vendorID;
    deviceID = (*it).deviceID;

    return true;
}

/**
 * Parse /proc/bus/pci/devices into a map:
 *  busidstring -> vendorid/deviceid
 *
 * busidstring is the bus id, device id and function in bus:device:func format
 */
bool PCIInfo::initStatic()
{
    if ( s_initialized )
        return true;

    s_initialized = true;

    if ( s_procBusPCIDevicesList )
        return true;

    s_procBusPCIDevicesList = pciinfosd.setObject( new DeviceInfoMap );

#if defined(HAVE_LINUX_PCI_H)
    QFile f( "/proc/bus/pci/devices" );
    if ( !f.open( IO_ReadOnly ) )
        return false; // no pci support

    QTextStream stream( &f );
    while ( !f.atEnd() )
    {
        QString line = stream.readLine();

        QStringList splittedLine = QStringList::split( '\t', line );

        if ( splittedLine.count() < 2 || splittedLine[ 1 ].length() != 8 )
        {
            KMessageBox::error( 0, i18n( "Invalid format of /proc/bus/pci/devices. Aborting." ) );
            ::exit( 1 );
        }

        uint vendorID = splittedLine[ 1 ].left( 4 ).toUInt( 0, 16 );
        uint deviceID = splittedLine[ 1 ].mid( 4 ).toUInt( 0, 16 );

        // the bus id is in hexadecimal format. the highest 8 bits define the bus number.
        // (so we strip out the first two hex characters :)
        QString busID = QString::number( splittedLine[ 0 ].left( 2 ).toUShort() ); // convert back and forth to get rid of the leading zeroes. /me lazy

        ushort devfn = splittedLine[ 0 ].mid( 2 ).toUShort( 0, 16 );
        // the slot and function id is encoded like in one byte:
        QString slot = QString::number( PCI_SLOT( devfn ) );
        QString func = QString::number( PCI_FUNC( devfn ) );

        QString busIDString = busID + ':' + slot + ':' + func;

	// fprintf(stderr,"%s -> %x %x\n", busIDString.latin1(),vendorID, deviceID);

        s_procBusPCIDevicesList->insert( busIDString, DeviceInfo( vendorID, deviceID ) );
    }

    f.close();
    return true;
#else
    return false;
#endif 
}
