/*
 *  PCI support functions
 *  Copyright (c) by Jaroslav Kysela <perex@jcu.cz>
 *
 *
 *   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 "driver.h"
#include "sndpci.h"

#ifdef CONFIG_PCI

#ifndef LINUX_2_1

int snd_pci_find_device( unsigned int vendor, unsigned int device, unsigned int index, struct snd_pci_dev *dev )
{
  unsigned char bus, devfn;
  unsigned char cmd, tmp;
  unsigned int l;
  int idx;
 
  if ( !dev || index < 0 ) return -EINVAL;
  if ( pcibios_find_device( vendor, device, index, &bus, &devfn ) == 0 ) {
    memset( dev, 0, sizeof( struct snd_pci_dev ) );
    dev -> bus = bus;
    dev -> devfn = devfn;
    dev -> vendor = vendor;
    dev -> device = device;
    /* non-destructively determine if device can be a master: */
    snd_pci_read_config_byte( dev, PCI_COMMAND, &cmd );
    snd_pci_write_config_byte( dev, PCI_COMMAND, cmd | PCI_COMMAND_MASTER );
    snd_pci_read_config_byte( dev, PCI_COMMAND, &tmp );
    dev->master = ((tmp & PCI_COMMAND_MASTER) != 0);
    snd_pci_write_config_byte( dev, PCI_COMMAND, cmd );
    /* check to see if this device is a PCI-PCI bridge: */
    snd_pci_read_config_dword( dev, PCI_CLASS_REVISION, &l );
    l = l >> 8;                     /* upper 3 bytes */
    dev -> class = l;
    /* determine interrupt line */
    snd_pci_read_config_byte( dev, PCI_INTERRUPT_LINE, ((unsigned char *)&dev -> irq) );
    /* determine base addresses */
    for ( idx = 0; idx < 6; idx++ )
      snd_pci_read_config_dword( dev, PCI_BASE_ADDRESS_0 + (idx << 2), ((unsigned int *)&dev -> base_address[ idx ]) );
    return 0;
  }
  return -ENODEV;
}

#else

int snd_pci_find_device( unsigned int vendor, unsigned int device, unsigned int index, struct snd_pci_dev *dev )
{
  struct pci_dev *pci_dev;
  int idx;
  
  if ( !dev || index < 0 ) return -EINVAL;
  pci_dev = NULL;
  do {
    pci_dev = pci_find_device( vendor, device, pci_dev );
    if ( pci_dev ) {
      if ( !index ) {
        memset( dev, 0, sizeof( struct snd_pci_dev ) );
        dev -> pci_dev = pci_dev;
        dev -> devfn = pci_dev -> devfn;
        dev -> vendor = vendor;
        dev -> device = device;
        dev -> class = pci_dev -> class;
        dev -> master = pci_dev -> master;
        dev -> irq = pci_dev -> irq;
        for ( idx = 0; idx < 6; idx++ )
          dev -> base_address[ idx ] = pci_dev -> base_address[ idx ];
        return 0;
      } else {
        index--;
      }
    }
  } while ( pci_dev );
  return -ENODEV;
}

#endif

#endif /* CONFIG_PCI */
