/*
   usbAccConfig.c
   copyright (c) 2013 Jeremy Rosen <jeremy.rosen@openwide.fr>

   usbAccConfig 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 3 of the License, or
   (at your option) any later version.

   usbAccConfig 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 usbAccConfig.  If not, see <http://www.gnu.org/licenses/>.
   if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, 
   Boston, MA  02110-1301  USA
 */


#include <stdio.h>
#include <usb.h>
#include <libusb.h>
#include <string.h>
#include <unistd.h>

// VID and PID of phones in Android accessory mode
#define GOOGLE_VID 0x18d1
#define ACCESSORY_PID_BASE 0x2D00


// Parameters passed to Android to recognise the device
#define MANUFACTURER "Openwide"
#define MODEL "TestUsbAccessory"
#define DESCRIPTION "A simple program to test the accessory mode of Android phones"
#define VERSION	"1.0"
#define URI "http://linuxembedded.fr"
#define SERIAL "N/A"


// Capabilities detected on the Android device
#define CAPABILITY_ACC 1<<0
#define CAPABILITY_ADB 1<<1
#define CAPABILITY_AUDIO 1<<2

typedef struct {
	int vid;
	int pid;
} usb_identifier;

usb_identifier skipped_id[] = {
	// hardwired : list of embedded peripherals of the raspberry-pi
	{0x0424,0x9512},
	{0x1d6b,0x0002},
	{0x0424,0xec00},
	{GOOGLE_VID,ACCESSORY_PID_BASE},
	{GOOGLE_VID,ACCESSORY_PID_BASE + 1},
	{GOOGLE_VID,ACCESSORY_PID_BASE + 2},
	{GOOGLE_VID,ACCESSORY_PID_BASE + 3},
	{GOOGLE_VID,ACCESSORY_PID_BASE + 4},
	{GOOGLE_VID,ACCESSORY_PID_BASE + 5},
	{0,0}
};

static int pollDevice(int vid, int pid);

int main (int argc, char *argv[]){
	libusb_init(NULL);
	if(argc < 3) {
		printf("no device vid pid on command line\n");
		return 0;
	} else {
		int vid = strtol(argv[1],NULL,0);
		int pid = strtol(argv[2],NULL,0);
		usb_identifier *id = skipped_id;
		while(id->pid || id->vid) {
			if(vid == id->vid && pid == id->pid) {
				printf("device skipped");
				return 0;
			}
			id++;
		}

		if(!pollDevice(vid,pid)) {
			libusb_exit(NULL);
			printf("Given device isn't an android device\n");
			return 0;
			
		}
	}
	printf( "Done, no errors. Device should reappear as an android accessory\n");
	libusb_exit(NULL);
	return 0;
}


// sends the correct sequence to check if it is an android device
// if it is, puts the device in accessory mode and return 1
// if it isn't, return 0

static int pollDevice(int vid, int pid) {
	unsigned char ioBuffer[2];
	int devVersion;
	int response=0;
	struct libusb_device_handle* handle;

	if((handle = libusb_open_device_with_vid_pid(NULL, vid, pid)) == NULL) goto error;
	// poll protocol : is this an android device ?
	response = libusb_control_transfer( handle, 0xC0, 51, 0, 0, ioBuffer, 2, 0);

	if(response < 0) goto error;

	devVersion = ioBuffer[1] << 8 | ioBuffer[0];
	printf("Version Code Device: %d\n", devVersion);
	if(devVersion == 0) {
		// not an android device
		goto error;
	}

	usleep(1000);//sometimes hangs on the next transfer :(

	/**** Comment the following block to disable Accessory mode ******/
#if 0
	response = libusb_control_transfer(handle,0x40,52,0,0,(unsigned char*)MANUFACTURER,strlen(MANUFACTURER)+1,0);
	if(response < 0) goto error;
	response = libusb_control_transfer(handle,0x40,52,0,1,(unsigned char*)MODEL,strlen(MODEL)+1,0);
	if(response < 0) goto error;
	response = libusb_control_transfer(handle,0x40,52,0,2,(unsigned char*)DESCRIPTION,strlen(DESCRIPTION)+1,0);
	if(response < 0) goto error;
	response = libusb_control_transfer(handle,0x40,52,0,3,(unsigned char*)VERSION,strlen(VERSION)+1,0);
	if(response < 0) goto error;
	response = libusb_control_transfer(handle,0x40,52,0,4,(unsigned char*)URI,strlen(URI)+1,0);
	if(response < 0) goto error;
	response = libusb_control_transfer(handle,0x40,52,0,5,(unsigned char*)SERIAL,strlen(SERIAL)+1,0);
	if(response < 0) goto error;
#endif


	/***** Comment the following block to disable audio accessory mode ***/
#if 1
	if(devVersion >= 2) {
		printf("Sending Audio mode request\n");
		response = libusb_control_transfer(handle,0x40,58,1,0,NULL,0,0);
		if(response < 0) goto error;
	}
#endif
	/**** fin de la section �� supprimer ****/

	printf("Sending Accessory Identification \n");

	response = libusb_control_transfer(handle,0x40,53,0,0,NULL,0,0);
	if(response < 0) goto error;

	libusb_close(handle);
	return 1;
error:
	if(response) printf("USB error : %s\n",libusb_error_name(response));
	libusb_close(handle);
	return 0;
}


