/*
 * winusb.c
 *
 *  Created on: Apr 22, 2011
 *      Author: Jan Axelson
 *
 * Demonstrates communicating with a device designed for use with a vendor-defined host driver
 * such as WinUSB.
 * Sends and receives data using control, bulk, and interrupt transfers.
 * Requires: an attached device that supports these transfer types.
 * The device firmware should respond to received data by sending data back to the host.
 * The application sends two vendor-defined control transfers.
 * Request 1 is a control write transfer that sends data to the device.
 * Request 2 is a control read transfer that requests data from the device.
 * The application sends data using a bulk OUT transfer and requests data using a bulk IN transfer.
 * The application sends data using an interrupt OUT transfer and requests data using an interrupt IN transfer.
 * Change VENDOR_ID and PRODUCT_ID to match your device's Vendor ID and Product ID.
 * Assumes bulk endpoint 1 IN and OUT and interrupt endpoint 2 IN and OUT and two vendor-defined control requests.
 * See Lvr.com/winusb.htm for example device firmware.
 * This firmware is adapted from code provided by Xiaofan.
 * Note: libusb error codes are negative numbers.

The application uses the libusb 1.0 API from libusb.org.
Compile the application with the -lusb-1.0 option. 
Use the -I option if needed to specify the path to the libusb.h header file. For example:
-I/usr/local/angstrom/arm/arm-angstrom-linux-gnueabi/usr/include/libusb-1.0 

 */

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <libusb.h>

static const int INTERFACE_NUMBER = 0;
static const int TIMEOUT_MS = 5000;

int exchange_data_via_bulk_transfers(libusb_device_handle *devh);
int exchange_data_via_control_transfers(libusb_device_handle *devh);
int exchange_data_via_interrupt_transfers(libusb_device_handle *devh);

int main(void)
{
	// Change these as needed to match idVendor and idProduct in your device's device descriptor.

	static const int VENDOR_ID = 0x0925;
	static const int PRODUCT_ID = 0x1456;

	struct libusb_device_handle *devh = NULL;
	int device_ready = 0;
	int result;

	result = libusb_init(NULL);
	if (result >= 0)
	{
		devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);

		if (devh != NULL)
		{
			{
				result = libusb_claim_interface(devh, INTERFACE_NUMBER);
				if (result >= 0)
				{
					device_ready = 1;
				}
				else
				{
					fprintf(stderr, "libusb_claim_interface error %d\n", result);
				}
			}
		}
		else
		{
			fprintf(stderr, "Unable to find the device.\n");
		}
	}
	else
	{
		fprintf(stderr, "Unable to initialize libusb.\n");
	}

	if (device_ready)
	{
		// Send and receive data.

		exchange_data_via_control_transfers(devh);
		exchange_data_via_interrupt_transfers(devh);
		exchange_data_via_bulk_transfers(devh);

		// Finished using the device.

		libusb_release_interface(devh, 0);
	}

	libusb_close(devh);
	libusb_exit(NULL);
	return 0;
}

// Use bulk transfers to write data to the device and receive data from the device.
// Returns - zero on success, libusb error code on failure.

int exchange_data_via_bulk_transfers(libusb_device_handle *devh)
{
	// Assumes bulk endpoint 1 IN and OUT:

	static const int BULK_IN_ENDPOINT = 0x81;
	static const int BULK_OUT_ENDPOINT = 0x01;

	// With firmware support, transfers can be > the endpoint's max packet size.

	static const int MAX_BULK_IN_TRANSFER_SIZE = 64;
	static const int MAX_BULK_OUT_TRANSFER_SIZE = 64;

	int bytes_transferred;
	char data_in[MAX_BULK_IN_TRANSFER_SIZE];
	char data_out[MAX_BULK_OUT_TRANSFER_SIZE];
	int i;
	int result = 0;

	// Store data in a buffer for sending.
	// This example uses string data.

	strcpy(data_out, "Bulk transfer test data.");

	// Write data to the device.

	result = libusb_bulk_transfer(
			devh,
			BULK_OUT_ENDPOINT,
			data_out,
			strlen(data_out),
			&bytes_transferred,
			TIMEOUT_MS);

	if (result >= 0)
	{
	  	printf("Data sent via bulk transfer:\n");

	  	for(i = 0; i < bytes_transferred; i++)
	  	{
	  		printf("%c",data_out[i]);
	  	}
	  	printf("\n");

		// Read data from the device.

	  	result = libusb_bulk_transfer(
				devh,
				BULK_IN_ENDPOINT,
				data_in,
				MAX_BULK_IN_TRANSFER_SIZE,
				&bytes_transferred,
				TIMEOUT_MS);

		if (result >= 0)
		{
			if (bytes_transferred > 0)
			{
			  	printf("Data received via bulk transfer:\n");

				for(i = 0; i < bytes_transferred; i++)
				{
					printf("%c",data_in[i]);
				}
				printf("\n");
			}
			else
			{
				fprintf(stderr, "No data received in bulk transfer (%d)\n", result);
				return -1;
			}
		}
		else
		{
			fprintf(stderr, "Error receiving data via bulk transfer %d\n", result);
			return result;
		}
	}
	else
	{
		fprintf(stderr, "Error sending data via bulk transfer %d\n", result);
		return result;
	}
  	return 0;
 }

// Use control transfers to write data to the device and receive data from the device.
// Returns - zero on success, libusb error code on failure.

int exchange_data_via_control_transfers(libusb_device_handle *devh)
{
	// Values for bmRequestType in the Setup transaction's Data packet.

	static const int CONTROL_REQUEST_TYPE_IN = LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_INTERFACE;
	static const int CONTROL_REQUEST_TYPE_OUT = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_INTERFACE;

	// With firmware support, transfers can be > the endpoint's max packet size.

	static const int MAX_CONTROL_IN_TRANSFER_SIZE = 8;
	static const int MAX_CONTROL_OUT_TRANSFER_SIZE = 8;

	// The device supports two vendor-specific control requests:

	static const int WINUSB_REQUEST_1 = 0x01;
	static const int WINUSB_REQUEST_2 = 0x02;

	int bytes_received;
	int bytes_sent;
	char data_in[MAX_CONTROL_IN_TRANSFER_SIZE];
	char data_out[MAX_CONTROL_OUT_TRANSFER_SIZE];	int i = 0;
	int result = 0;

	// Store example data in the output buffer for sending.
	// This example uses binary data.

	for (i=0;i < MAX_CONTROL_OUT_TRANSFER_SIZE; i++)
	{
		data_out[i]=0x30+i;
	}

	// Send data to the device.

	bytes_sent = libusb_control_transfer(
			devh,
			CONTROL_REQUEST_TYPE_OUT ,
			WINUSB_REQUEST_1,
			0,
			INTERFACE_NUMBER,
			data_out,
			sizeof(data_out),
			TIMEOUT_MS);

	if (bytes_sent >= 0)
	{
	 	printf("Data sent via control transfer:\n");
	 	for(i = 0; i < bytes_sent; i++)
	 	{
	 		printf("%02x ",data_out[i]);
	 	}
	 	printf("\n");

		// Request data from the device.

		bytes_received = libusb_control_transfer(
				devh,
				CONTROL_REQUEST_TYPE_IN ,
				WINUSB_REQUEST_2,
				0,
				INTERFACE_NUMBER,
				data_in,
				MAX_CONTROL_IN_TRANSFER_SIZE,
				TIMEOUT_MS);

		if (bytes_received >= 0)
		{
		 	printf("Data received via control transfer:\n");
		 	for(i = 0; i < bytes_received; i++)
		 	{
		 		printf("%02x ",data_in[i]);
		 	}
			printf("\n");
		}
		else
		{
			fprintf(stderr, "Error receiving data via control transfer %d\n", result);
			return result;
		}
	}
	else
	{
		fprintf(stderr, "Error sending data via control transfer %d\n", result);
		return result;
	}
	return 0;
 }

// Use interrupt transfers to to write data to the device and receive data from the device.
// Returns - zero on success, libusb error code on failure.

int exchange_data_via_interrupt_transfers(libusb_device_handle *devh)
{

	// Assumes interrupt endpoint 2 IN and OUT:

	static const int INTERRUPT_IN_ENDPOINT = 0x82;
	static const int INTERRUPT_OUT_ENDPOINT = 0x02;

	// With firmware support, transfers can be > the endpoint's max packet size.

	static const int MAX_INTERRUPT_IN_TRANSFER_SIZE = 2;
	static const int MAX_INTERRUPT_OUT_TRANSFER_SIZE = 2;

	int bytes_transferred;
	int i = 0;
	int result = 0;;

 	char data_in[MAX_INTERRUPT_IN_TRANSFER_SIZE];
	char data_out[MAX_INTERRUPT_OUT_TRANSFER_SIZE];

	// Store data in a buffer for sending.
	// This example uses binary data.

	for (i=0;i < MAX_INTERRUPT_OUT_TRANSFER_SIZE; i++)
	{
		data_out[i]=0x40+i;
	}
	// Write data to the device.

	result = libusb_interrupt_transfer(
			devh,
			INTERRUPT_OUT_ENDPOINT,
			data_out,
			MAX_INTERRUPT_OUT_TRANSFER_SIZE,
			&bytes_transferred,
			TIMEOUT_MS);

	if (result >= 0)
	{
	  	printf("Data sent via interrupt transfer:\n");
	  	for(i = 0; i < bytes_transferred; i++)
	  	{
	  		printf("%02x ",data_out[i]);
	  	}
	  	printf("\n");

		// Read data from the device.

		result = libusb_interrupt_transfer(
				devh,
				INTERRUPT_IN_ENDPOINT,
				data_in,
				MAX_INTERRUPT_OUT_TRANSFER_SIZE,
				&bytes_transferred,
				TIMEOUT_MS);

		if (result >= 0)
		{
			if (bytes_transferred > 0)
			{
			  	printf("Data received via interrupt transfer:\n");
			  	for(i = 0; i < bytes_transferred; i++)
			  	{
			  		printf("%02x ",data_in[i]);
			  	}
			  	printf("\n");
			}
			else
			{
				fprintf(stderr, "No data received in interrupt transfer (%d)\n", result);
				return -1;
			}
		}
		else
		{
			fprintf(stderr, "Error receiving data via interrupt transfer %d\n", result);
			return result;
		}
	}
	else
	{
		fprintf(stderr, "Error sending data via interrupt transfer %d\n", result);
		return result;
	}
  	return 0;
 }
