/*
 * Copyright (c) 2006 Alvaro Lopes <alvieboy@alvie.com>
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "autodetect.h"
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>

#define PCI_VENDOR_OPTION_NV 0x1931
#define PCI_DEVICE_QUALCOMM_MSM6275 0x000c

#define USB_VENDOR_OPTION 0x0af0
#define USB_DEVICE_OPTION_UMTS_CARD 0x5000
#define USB_DEVICE_OPTION_FUSION_CARD 0x6300

#define NOZOMI_DEVICE "/dev/noz2"


struct device_info *device_info_new()
{
    struct device_info *info;

    info = g_new0( struct device_info, 1 );

    return info;
}

void device_info_free( struct device_info *i )
{
    if ( i->device != NULL )
	g_free ( i->device );
    if ( i->device_description != NULL )
	g_free ( i->device_description );
    if ( i->error_reason != NULL )
	g_free ( i->error_reason );
    g_free( i );

}

gboolean nozomi_detect( struct known_device *dev )
{
    struct stat st;
    /*   int fd;*/

    if ( ! dev->info )
	dev->info = device_info_new();

    if ( dev->info->error_reason )
	g_free(dev->info->error_reason);

    dev->info->error_reason = NULL;

    if ( stat( NOZOMI_DEVICE, &st ) < 0 )
    {
	/* Does not exist?? */
        dev->info->error_reason = g_strdup( g_strerror(errno) );
        return FALSE;
    }

    /* just try to open the device */

    dev->info->device = g_strdup( NOZOMI_DEVICE );

    return TRUE;
}

gboolean serial_detect( struct known_device *dev )
{
    return FALSE;
}


static struct known_device devs[] =
{
    {
	"Option N.V. Qualcomm MSM6275 UMTS chip",
	"Generic Driver",
	DEVICE_TYPE_PCI,
	PCI_VENDOR_OPTION_NV,
	PCI_DEVICE_QUALCOMM_MSM6275,
	&nozomi_detect
    },

    {
	"Option UMTS device",
	"Generic Driver",
	DEVICE_TYPE_USB,
	USB_VENDOR_OPTION,
	USB_DEVICE_OPTION_UMTS_CARD,
	&serial_detect
    },

    { NULL, NULL, DEVICE_TYPE_NONE, 0, 0, NULL }
};

void free_single_device( struct known_device *d, void *data )
{
    if (d==NULL)
        return;
    if ( d->info == NULL )
	return;
    device_info_free( d->info );

    d->info = NULL;

}

void free_detected_devices( GList *l )
{
    g_list_foreach( l, (GFunc) free_single_device, NULL );

    g_list_free( l );
}



static GList *detect_pci_devices(GList *l)
{
#ifdef HAVE_PCIUTILS
    struct pci_access *pcia;
    struct pci_dev *pdev;
    struct known_device *d;

    pcia = pci_alloc();

    if ( NULL == pcia )
	return NULL;

    pci_init( pcia );

    pci_scan_bus( pcia );

    if ( pcia->devices == NULL ) {
        return NULL;
    }

    for ( pdev = pcia->devices; pdev; pdev=pdev->next ) {

	for ( d = devs; d->description; d++ ) {

	    if ( d->type == DEVICE_TYPE_PCI &&
		pdev->vendor_id == d->vendor &&
		pdev->device_id == d->product )
	    {
		printf("Found PCI device '%s'\n", d->description);
		l = g_list_append( l, d );
                d->detect_device( d );
	    }
	}
    }

/* pci_exit_cleanup: */
    pci_cleanup( pcia );
#endif
    return l;
}

static GList *detect_usb_devices(GList *l)
{
#ifdef HAVE_USBUTILS
    struct usb_bus *bus;
    struct usb_device *dev;
    struct known_device *d;

    usb_init();
    usb_find_busses();
    usb_find_devices();

    bus = usb_get_busses();

    while ( bus ) {
	dev = bus->devices;
	while (dev) {
	    fprintf(stderr,"%s\n", dev->filename );
	    for ( d = devs; d->description; d++ ) {

		if ( d->type == DEVICE_TYPE_USB &&
		    dev->descriptor.idVendor == d->vendor &&
		    dev->descriptor.idProduct == d->product )
		{
		    printf("Found USB device '%s'\n", d->description);
		    l = g_list_append( l, d );

		    /* Fill in the real name of the device */

		    d->detect_device( d );
		}
	    }
	    dev=dev->next;
	}
	bus = bus->next;
    }

#endif
    return l;
}

GList *detect_devices()
{
    GList *l = NULL;
    l = detect_pci_devices( l );
    l = detect_usb_devices( l );
    return l;
}
