#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#include <usb.h>
#define TIMEOUT 300

static void
read_fw (struct usb_dev_handle *dev, char *filename)
{
	long long int pos;
	int fd = open (filename, O_RDONLY), rd, len, req, llen, res;
	unsigned char data[4], rdata[1024], *ptr;
	if (fd == -1) {
		perror ("Opening file");
		return;
	} else if (lseek (fd, 5172, SEEK_SET) != 5172) {
		perror ("Seeking");
		return;
	}

	if ((res = usb_control_msg (dev, 0x40, 0xA0, 0xe600, 0,
				    "\1", 1, TIMEOUT)) != 1) {
		fprintf (stderr, "Firmware load init failed: %s\n",
			 strerror (errno));
		return;
	}
	while (1) {
		if ((len = read (fd, data, 4)) != 4) {
			if (len == 0) {
				fprintf (stderr, "eos\n");
				return;
			} else {
				perror("Header read");
				return;
			}
		}
		len = (data[0] << 8) | data[1];
		req = (data[2] << 8) | data[3];
		if (len == 0x8001)
			break;
		else if (len == 0)
			continue;
		else if (len < 0 || len >= 1024) {
			fprintf (stderr,
				 "Invalid firmware length %d, load aborted\n",
				 len);
			goto end;
		} else if (read (fd, rdata, len) != len) {
			perror ("Error reading firmware data");
			goto end;
		}

		/* upload to usb bus */
		for (ptr = rdata; len > 0; req += 50, ptr += 50) {
			llen = len > 50 ? 50 : len;
			len -= llen;

			if ((res = usb_control_msg (dev, 0x40, 0xA0, req, 0,
						    (char *) ptr, llen,
						    TIMEOUT)) != llen) {
				fprintf (stderr,
					 "firmware load req=0x%x failed: %s\n",
					 req, strerror (errno));
				goto end;
			}
		}
	}

end:
	if ((res = usb_control_msg (dev, 0x40, 0xA0, 0xe600, 0,
				    "\0", 1, TIMEOUT)) != 1) {
		fprintf (stderr, "Firmware finish failed: %s\n",
			 strerror (errno));
	}
}

static void
probe_dev (struct usb_device *dev, char *fw_filename)
{
	int n;

	printf ("Once device 0x%x 0x%x\n", dev->descriptor.idVendor, dev->descriptor.idProduct);
	if (dev->descriptor.idVendor == 0x05ac &&
	    dev->descriptor.idProduct == 0x8300 &&
	    dev->descriptor.bDeviceClass == 0xff &&
	    dev->descriptor.bDeviceSubClass == 0xff &&
	    dev->descriptor.bDeviceProtocol == 0xff) {
		usb_dev_handle *h;

		/* load firmware */
		if (!(h = usb_open (dev))) {
			perror ("Opening iSight");
			return;
		}
		printf ("Loading firmware for one iSight\n");
		read_fw (h, fw_filename);
		printf ("Loaded firmware for one iSight\n");
		usb_close (h);
	} else if (dev->descriptor.idVendor == 0x05ac &&
		   dev->descriptor.idProduct == 0x8501) {
		printf ("One iSight with firmware already loaded found\n");
	}

	for (n = 0; n < dev->num_children; n++)
		probe_dev (dev->children[n], fw_filename);
}

int
main (int argc, char *argv[])
{
    int busnum, devicenum;
	struct usb_bus *busses;
    struct usb_bus *bus;
    if (argc != 2) {
		fprintf(stderr, "Usage: %s <firmware file>\n", argv[0]);
		fprintf(stderr, "Firmware can usually be found on your Mac "
			"partition in /System/Library/Extensions/"
			"IOUSBFamily.kext/Contents/PlugIns/"
			"AppleUSBVideoSupport.kext/Contents/MacOS/"
			"AppleUSBVideoSupport\n");
		return -1;
	}

	/* init usb */
	usb_init ();

	if ((busnum = usb_find_busses ()) == 0) {
		fprintf (stderr, "No USB devices found\n");
		return -1;
	}
    fprintf(stderr, "%d buses found\n", busnum);
	devicenum = usb_find_devices ();
    fprintf(stderr, "%d USB devices found\n", devicenum);
    busses = usb_get_busses();
	/* find us */
	for (bus = busses; bus ; bus = bus->next) {
		if (bus->devices != NULL) {
            struct usb_device *dev;
            for (dev = bus->devices; dev; dev = dev->next) {
			    probe_dev (dev, argv[1]);
            }
		}
	}

	return 0;
}
