// USB_IP_LIB.h
#include "aes.h"
#include "config.h"
#include <stdint.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <Ws2tcpip.h> 
#include <signal.h>
#include <windows.h>
#include <setupapi.h>
#include <initguid.h>
#include <winioctl.h>
#include "names.h"
#include <Winsock2.h>


#pragma once

using namespace System;
using namespace System::Threading;

DEFINE_GUID (GUID_DEVINTERFACE_BUSENUM_USBIP,
        0xD35F7840, 0x6A0C, 0x11d2, 0xB8, 0x41, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71);

#define OUT_Q_LEN 256
#define UNUSED(x) (void)(x)
#define snprintf _snprintf
#define OP_UNSPEC	0x00
#define BIG_SIZE 1000000
#define USBIP_PORT			3240
#define USBIP_PORT_STRING  "3240"
#define OP_DEVLIST	0x05
#define OP_REQ_DEVLIST	(OP_REQUEST | OP_DEVLIST)
#define OP_REP_DEVLIST	(OP_REPLY   | OP_DEVLIST)
#define OP_IMPORT	0x03
#define OP_REQ_IMPORT	(OP_REQUEST | OP_IMPORT)
#define OP_REP_IMPORT   (OP_REPLY   | OP_IMPORT)
#define SYSFS_PATH_MAX		256
#define SYSFS_BUS_ID_SIZE	32

#define USBVBUS_IOCTL(_index_) \
    CTL_CODE (FILE_DEVICE_BUS_EXTENDER, _index_, METHOD_BUFFERED, FILE_READ_DATA)

#define IOCTL_USBVBUS_PLUGIN_HARDWARE               USBVBUS_IOCTL (0x0)
#define IOCTL_USBVBUS_UNPLUG_HARDWARE               USBVBUS_IOCTL (0x1)
#define IOCTL_USBVBUS_EJECT_HARDWARE                USBVBUS_IOCTL (0x2)
#define IOCTL_USBVBUS_GET_PORTS_STATUS              USBVBUS_IOCTL (0x3)

#define PACK_OP_IMPORT_REQUEST(pack, request)  do {\
} while (0)
#define PACK_OP_DEVLIST_REPLY(pack, reply)  do {\
	pack_uint32_t(pack, &(reply)->ndev);\
} while (0)
#define PACK_OP_IMPORT_REPLY(pack, reply)  do {\
	pack_usb_device(pack, &(reply)->udev);\
} while (0)

#define PACK_OP_COMMON(pack, op_common)  do {\
	pack_uint16_t(pack, &(op_common)->version);\
	pack_uint16_t(pack, &(op_common)->code   );\
	pack_uint32_t(pack, &(op_common)->status );\
} while (0)

typedef struct _ioctl_usbvbus_plugin
{
	unsigned int devid;
	/* 4 bytes */
	unsigned short vendor;
	unsigned short product;
	/* 8 bytes */
	unsigned short version;
	unsigned char speed;
	unsigned char inum;
	/* 12 bytes */
	unsigned char int0_class;
	unsigned char int0_subclass;
	unsigned char int0_protocol;
	signed char addr;  /* then it can not be bigger then 127 */
	/* 16 bytes */
} ioctl_usbvbus_plugin;

typedef struct _ioctl_usbvbus_get_ports_status
{
	union {
		signed char max_used_port; /* then it can not be bigger than 127 */
		unsigned char port_status[128];
		/* 128 bytes */
	};
} ioctl_usbvbus_get_ports_status;

typedef signed int ssize_t;
struct usb_interface {
	uint8_t bInterfaceClass;
	uint8_t bInterfaceSubClass;
	uint8_t bInterfaceProtocol;
	uint8_t padding;	/* alignment */
};
struct op_devlist_reply {
	uint32_t ndev;
	/* followed by reply_extra[] */
};
struct op_common {
	uint16_t version;

#define OP_REQUEST	(0x80 << 8)
#define OP_REPLY	(0x00 << 8)
	uint16_t code;

	/* add more error code */
#define ST_OK	0x00
#define ST_NA	0x01
	uint32_t status; /* op_code status (for reply) */

};
struct usb_device {
	char path[SYSFS_PATH_MAX];
	char busid[SYSFS_BUS_ID_SIZE];

	uint32_t busnum;
	uint32_t devnum;
	uint32_t speed;

	uint16_t idVendor;
	uint16_t idProduct;
	uint16_t bcdDevice;

	uint8_t bDeviceClass;
	uint8_t bDeviceSubClass;
	uint8_t bDeviceProtocol;
	uint8_t bConfigurationValue;
	uint8_t bNumConfigurations;
	uint8_t bNumInterfaces;
};
struct op_import_request {
	char busid[SYSFS_BUS_ID_SIZE];
};
struct op_import_reply {
	struct usb_device udev;
//	struct usb_interface uinf[];
};

typedef struct _ioctl_usbvbus_unplug
{
	signed char addr;
	char unused[3];

} ioctl_usbvbus_unplug;
typedef uint32_t __u32;
typedef int32_t __s32;
struct usbip_header_basic {
#define USBIP_CMD_SUBMIT	0x0001
#define USBIP_CMD_UNLINK	0x0002
#define USBIP_RET_SUBMIT	0x0003
#define USBIP_RET_UNLINK	0x0004
	__u32 command;

	 /* sequential number which identifies requests.
	  * incremented per connections */
	__u32 seqnum;

	/* devid is used to specify a remote USB device uniquely instead
	 * of busnum and devnum in Linux. In the case of Linux stub_driver,
	 * this value is ((busnum << 16) | devnum) */
	__u32 devid;

#define USBIP_DIR_OUT	0
#define USBIP_DIR_IN	1
	__u32 direction;
	__u32 ep;     /* endpoint number */
};
struct usbip_header_cmd_submit {
	/* these values are basically the same as in a URB. */

	/* the same in a URB. */
	__u32 transfer_flags;

	/* set the following data size (out),
	 * or expected reading data size (in) */
	__s32 transfer_buffer_length;

	/* it is difficult for usbip to sync frames (reserved only?) */
	__s32 start_frame;

	/* the number of iso descriptors that follows this header */
	__s32 number_of_packets;

	/* the maximum time within which this request works in a host
	 * controller of a server side */
	__s32 interval;

	/* set setup packet data for a CTRL request */
	unsigned char setup[8];
};
struct usbip_header_ret_submit {
	__s32 status;
	__s32 actual_length;		/* returned data length */
	__s32 start_frame;		/* ISO and INT */
	__s32 number_of_packets;	/* ISO only */
	__s32 error_count;		/* ISO only */
};
struct usbip_header_cmd_unlink {
	__u32 seqnum;			/* URB's seqnum that will be unlinked */
};
struct usbip_header_ret_unlink {
	__s32 status;
};
struct usbip_header {
	struct usbip_header_basic base;

	union {
		struct usbip_header_cmd_submit	cmd_submit;
		struct usbip_header_ret_submit	ret_submit;
		struct usbip_header_cmd_unlink	cmd_unlink;
		struct usbip_header_ret_unlink	ret_unlink;
	} u;
};
struct usbip_iso_packet_descriptor {
	__u32 offset;
	__u32 length;			/* expected length */
	__u32 actual_length;
	__u32 status;
};

//static volatile int signalflag = 0;



//static long out_q_seqnum_array[OUT_Q_LEN];

#pragma once

using namespace System;

namespace USB_IP_LIB {

	public ref class Class1
	{	
	public: bool PRIPOJENE;
	public: long stav;
	//public: int lasterr;
	private: long *out_q_seqnum_array;
	private: char *dev_read_buf;
	private: char *sock_read_buf;
	//HANDLE hthread;
	public:	Class1()
			{
				PRIPOJENE=false;
				stav=0;
				//lasterr=0;
				out_q_seqnum_array=(long*) malloc(sizeof(long)*OUT_Q_LEN);
				//hthread=nullptr;
			}
	public:	~Class1()
			{
				PRIPOJENE=false;
				free((void *)out_q_seqnum_array);
				//if (hthread!=nullptr)
				//hthread=nullptr;
			}

	private: int Timed_Listener(int time,int port){
		int sockfd,newsockfd;
		socklen_t clilen;
		struct sockaddr_in serv_addr, cli_addr;
		struct timeval timeout;

		if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
		{
			int error= WSAGetLastError();
			printf("error creating listen socket %d \n\r", error );
			//exit(-1);
			return -1;//error
		}
		for (int i=0;i<sizeof(serv_addr);i++)
			((char *)&serv_addr)[i]=0;
		serv_addr.sin_family = AF_INET;
		serv_addr.sin_addr.s_addr = INADDR_ANY;
		serv_addr.sin_port = htons(port);
		if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
		{
		  printf("ERROR on binding\n\r");
		  return -1;//error
		}
		listen(sockfd,5);

		fd_set ReadFDs, WriteFDs, ExceptFDs;
		FD_ZERO(&ReadFDs);
		FD_ZERO(&WriteFDs);
		FD_ZERO(&ExceptFDs);

		// Add the listener socket to the read and except FD sets, if there
		// is one.	
		FD_SET(sockfd, &ReadFDs);
		FD_SET(sockfd, &ExceptFDs);
		
		timeout.tv_sec = 10;
		timeout.tv_usec = 0;
		int ret = select(0, &ReadFDs, &WriteFDs, &ExceptFDs, &timeout);
		if (ret< 0){
			printf("ERROR setsockopt failed\n");
			return -1;//error
		} 
		if (ret == 0)
			return -1;//timeout

		

		/*
		DWORD tim =time;
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
		if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tim,sizeof(DWORD)) < 0)
		{
			printf("ERROR setsockopt failed\n");
			return -1;//error
		}
		if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tim, sizeof(DWORD)) < 0)
        {
			printf("ERROR setsockopt failed\n");
			return -1;//error
		}
		*/

		clilen = sizeof(cli_addr);

		newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&clilen);
		closesocket(sockfd);
		return newsockfd;
	}
	
	public: int attach_device(char * host, char * busid,char* pass,int mode,unsigned long long conectingID)//Mode(1)-client Mode(2)-conect(server) Mode(3)conect
{  	
	SOCKET sockfd;
	int rhport;
	HANDLE devfd=INVALID_HANDLE_VALUE;
	struct usb_interface uinf;
	this->PRIPOJENE=true;

	if (mode<1 || mode>3)
		return -11;
	
	if (mode==2 && conectingID ==0 )
		return -1;


	for (int i=0;i<OUT_Q_LEN;i++)
			out_q_seqnum_array[i]=0;

	int err = init_socket();
	if (err) {
		return -1;
	}

	if (mode==1) //start listener 
	{
		sockfd = Timed_Listener(10,USBIP_PORT);
		if (sockfd==0)
			return -7;	//TIMEOUT
	}

	if (mode==3 || mode ==2){ 
		sockfd = tcp_connect(host, USBIP_PORT_STRING);
	}
	if (sockfd<=0 || sockfd==INVALID_SOCKET) 
	{
		fprintf(stderr,"tcp connect");
		return 0;
	}
	unsigned char buf[24];
	
	if (mode==2){
		*(unsigned long long*)buf=conectingID;
		usbip_send(sockfd,buf, 16);
	}
	usbip_recv(sockfd,buf, 24);
	if ((*(unsigned long long*)(buf+16))==0)//kontrola či je spojenie OK
	{
		closesocket(sockfd);
		return -8;	//nespravne alebo 6iadne yariadenie
	}

	unsigned char out[16];
	unsigned char expkey[4 * Nb * (Nr + 1)];
    
	AES::ExpandKey ((unsigned char*)pass, expkey);
	AES::Decrypt(buf,expkey,out);
	for (int i=0;i<16;i++){
		buf[i]=out[i];
	}
	usbip_send(sockfd,buf, 16);
	usbip_recv(sockfd,buf, 3);
	if (buf[0]!='O')
		return -10;	//nespravne heslo
	//unsigned short buf[10]={'S','T','A','R','T'};
	
	*(unsigned long long*)((void*)buf)=0xBDBE5751;
    // *(long long*)(void*)buf = get_ID();
	Sleep(200);
//	int retur =send(sockfd,(const char *)buf,8,NULL);
    //err=usbip_net_send(sockfd,(void*)buf,10);
	Sleep(200);

	rhport = query_import_device(sockfd, busid, &uinf, &devfd);
	if (rhport < 0) {
		fprintf(stderr,"query");
		return -9;
	}
	fprintf(stdout,"new usb device attached to usbvbus port %d\n", rhport);
	//v tejto funkcii zamrzne

	usbip_vbus_forward(sockfd, devfd);	
	//fprintf(stdout,"detaching device");
	usbip_vbus_detach_device(devfd,rhport);
	//fprintf(stdout,"closing connection to device");
	CloseHandle(devfd);
	//fprintf(stdout,"closing connection to peer");	
	closesocket(sockfd);
	//fprintf(stdout,"done");
	return 1;
}

//	private: static void THR_vlakno_run(System::Object ^ i)
//	{
//	((CLASLIB::Class1)(i))->PRIPOJENE=false;
//	return;
//	}
static int tcp_connect(char *hostname, char *service)
{
	struct addrinfo hints, *res, *res0;
	int sockfd;
	int err;


	memset(&hints, 0, sizeof(hints));
	hints.ai_socktype = SOCK_STREAM;

	/* get all possible addresses */
	err = getaddrinfo(hostname, service, &hints, &res0);
	if (err) {
		fprintf(stderr,"%s %s: %s", hostname, service, gai_strerror(err));
		return -1;
	}

	/* try all the addresses */
	for (res = res0; res; res = res->ai_next) {
		char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];

		err = getnameinfo(res->ai_addr, res->ai_addrlen,
				hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
		if (err) {
			fprintf(stderr,"%s %s: %s", hostname, service, gai_strerror(err));
			continue;
		}

		fprintf(stdout,"trying %s port %s\n", hbuf, sbuf);

		sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
		if (sockfd < 0) {
			fprintf(stderr,"socket");
			continue;
		}

		/* should set TCP_NODELAY for usbip */
		usbip_set_nodelay(sockfd);
		/* TODO: write code for heatbeat */
		usbip_set_keepalive(sockfd);

		err = connect(sockfd, res->ai_addr, res->ai_addrlen);
		if (err < 0) {
			closesocket(sockfd);
			continue;
		}

		/* connected */
		fprintf(stdout,"connected to %s:%s", hbuf, sbuf);
		freeaddrinfo(res0);
		return sockfd;
	}


	fprintf(stdout,"%s:%s, %s", hostname, service, "no destination to connect to");
	freeaddrinfo(res0);

	return -1;
}

static int query_interface0(SOCKET sockfd, char * busid, struct usb_interface * uinf0)
{
	int ret;
	struct op_devlist_reply rep;
	uint16_t code = OP_REP_DEVLIST;
	uint32_t i,j;
	char product_name[100];
	char class_name[100];
	struct usb_device udev;
	struct usb_interface uinf;
	int found=0;

	memset(&rep, 0, sizeof(rep));

	ret = usbip_send_op_common(sockfd, OP_REQ_DEVLIST, 0);
	if (ret < 0) {
		fprintf(stderr,"send op_common");
		return -1;
	}

	ret = usbip_recv_op_common(sockfd, &code);
	if (ret < 0) {
		fprintf(stderr,"recv op_common");
		return -1;
	}

	ret = usbip_recv(sockfd, (void *) &rep, sizeof(rep));
	if (ret < 0) {
		fprintf(stderr,"recv op_devlist");
		return -1;
	}

	PACK_OP_DEVLIST_REPLY(0, &rep);
	fprintf(stdout,"exportable %d devices", rep.ndev);

	for (i=0; i < rep.ndev; i++) {

		memset(&udev, 0, sizeof(udev));

		ret = usbip_recv(sockfd, (void *) &udev, sizeof(udev));
		if (ret < 0) {
			fprintf(stderr,"recv usb_device[%d]", i);
			return -1;
		}
		pack_usb_device(0, &udev);
		usbip_names_get_product(product_name, sizeof(product_name),
				udev.idVendor, udev.idProduct);
		usbip_names_get_class(class_name, sizeof(class_name), udev.bDeviceClass,
				udev.bDeviceSubClass, udev.bDeviceProtocol);

		fprintf(stdout,"%8s: %s", udev.busid, product_name);
		fprintf(stdout,"%8s: %s", " ", udev.path);
		fprintf(stdout,"%8s: %s", " ", class_name);

		for (j=0; j < udev.bNumInterfaces; j++) {

			ret = usbip_recv(sockfd, (void *) &uinf, sizeof(uinf));
			if (ret < 0) {
				fprintf(stderr,"recv usb_interface[%d]", j);
				return -1;
			}

			pack_usb_interface(0, &uinf);
			if(!strcmp(udev.busid, busid)&&j==0){
				memcpy(uinf0, &uinf, sizeof(uinf));
				found=1;
			}
			usbip_names_get_class(class_name, sizeof(class_name),
					uinf.bInterfaceClass,
					uinf.bInterfaceSubClass,
					uinf.bInterfaceProtocol);

			fprintf(stdout,"%8s: %2d - %s", " ", j, class_name);
		}

		fprintf(stdout," ");
	}
	if(found)
		return 0;
	return -1;
}

static int query_import_device(int sockfd, char *busid, struct usb_interface *uinf0, HANDLE * fd)
{
	int ret;
	struct op_import_request request;
	struct op_import_reply   reply;
	uint16_t code = OP_REP_IMPORT;

	memset(&request, 0, sizeof(request));
	memset(&reply, 0, sizeof(reply));

	/* send a request */
	ret = usbip_send_op_common(sockfd, OP_REQ_IMPORT, 0);
	if (ret < 0) {
		fprintf(stderr,"send op_common");
		return -1;
	}

	strncpy(request.busid, busid, sizeof(request.busid));
	request.busid[sizeof(request.busid)-1]=0;

	PACK_OP_IMPORT_REQUEST(0, &request);

	ret = usbip_send(sockfd, (void *) &request, sizeof(request));
	if (ret < 0) {
		fprintf(stderr,"send op_import_request");
		return -1;
	}

	/* recieve a reply */
	ret = usbip_recv_op_common(sockfd, &code);
	if (ret < 0) {
		fprintf(stderr,"recv op_common");
		return -1;
	}

	ret = usbip_recv(sockfd, (void *) &reply, sizeof(reply));
	if (ret < 0) {
		fprintf(stderr,"recv op_import_reply");
		return -1;
	}

	PACK_OP_IMPORT_REPLY(0, &reply);

	/* check the reply */
	if (strncmp(reply.udev.busid, busid, sizeof(reply.udev.busid))) {
		fprintf(stderr,"recv different busid %s", reply.udev.busid);
		return -1;
	}

	/* import a device */
	return import_device(sockfd, &reply.udev, uinf0, fd);
}

void usbip_vbus_forward(SOCKET sockfd, HANDLE devfd)
{
	HANDLE ev[3];
	OVERLAPPED ov[3];
	int ret;
	int i;
	int err=0;

	dev_read_buf = (char *)malloc(BIG_SIZE);
	sock_read_buf = (char *)malloc(BIG_SIZE);

	if(dev_read_buf == NULL||sock_read_buf==NULL){
		fprintf(stderr,"cannot allocate buffers");
		return;
	}

	for(i=0;i<3;i++){
		ev[i]=CreateEvent(NULL, FALSE, FALSE, NULL);
		if(NULL==ev[i]){
			fprintf(stderr,"cannot create new events");
			return;
		}
		ov[i].Offset=ov[i].OffsetHigh=0;
		ov[i].hEvent=ev[i];
	}

	signal(SIGINT,(void (__cdecl *)(int))signalhandler);

	dev_read_async(devfd, sockfd, &ov[0]);
	sock_read_async(sockfd, devfd, &ov[1], &ov[2]);

	do {
		//dbg_file("wait\n");																//removed
		ret =  WaitForMultipleObjects(2, ev, FALSE, 100);
//		dbg_file("wait out %d\n", ret);

		switch (ret) {
		case WAIT_TIMEOUT:
			// do nothing just give CTRL-C a chance to be detected
			break;
		case WAIT_OBJECT_0:
			stav=stav+1;
			err=dev_read_completed(devfd, sockfd, &ov[0]);
			break;
		case WAIT_OBJECT_0 + 1:
			err=sock_read_completed(sockfd, devfd, &ov[1], &ov[2]);
			break;
		default:
			fprintf(stderr,"unknown ret %d\n",ret);
			err=ret;
			break;
		}
	} while(err==0 && PRIPOJENE);
	
	//info("\n");
//	if (signalflag)
//	{
		//info("CTRL-C received\n");
//	}
	free(dev_read_buf);
	free(sock_read_buf);
	return;
}

static int usbip_vbus_detach_device(HANDLE fd, int port)
{	//problematická funkcia!!!!!!!!!!!!!!!!!!!!!!!!!!
	int ret;
	ioctl_usbvbus_unplug  unplug;
	unsigned long unused;

	unplug.addr = port;
	Sleep(100);
	if (fd==nullptr){
		Beep(1000,50);
		return -1;}
	try{
	ret = DeviceIoControl(fd, IOCTL_USBVBUS_UNPLUG_HARDWARE,
				&unplug, sizeof(unplug), NULL, 0, &unused, NULL);
	}
	catch(...)
	{
		Sleep(100);
		Beep(4000,50);
	}
	if(ret)
		return 0;
	return -1;
}

static int usbip_set_nodelay(int sockfd)
{
	const int val = 1;
	int ret;

	ret = setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (const char *)&val, sizeof(val));
	if (ret < 0)
		fprintf(stderr,"setsockopt TCP_NODELAY");

	return ret;
}

static int usbip_set_keepalive(int sockfd)
{
	const int val = 1;
	int ret;

	ret = setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (const char*)&val, sizeof(val));
	if (ret < 0)
		fprintf(stderr,"setsockopt SO_KEEPALIVE");

	return ret;
}

static int usbip_send_op_common(int sockfd, uint32_t code, uint32_t status)
{
	int ret;
	struct op_common op_common;

	memset(&op_common, 0, sizeof(op_common));

	op_common.version	= USBIP_VERSION;
	op_common.code		= code;
	op_common.status	= status;

	PACK_OP_COMMON(1, &op_common);

	ret = usbip_send(sockfd, (void *) &op_common, sizeof(op_common));
	if (ret < 0) {
		fprintf(stderr,"send op_common");
		return -1;
	}

	return 0;
}

static int usbip_recv_op_common(int sockfd, uint16_t *code)
{
	int ret;
	struct op_common op_common;

	memset(&op_common, '\0', sizeof(op_common));

	ret = usbip_recv(sockfd, (void *) &op_common, sizeof(op_common));
	if (ret < 0) {
		fprintf(stderr,"recv op_common, %d", ret);
		goto err;
	}

	PACK_OP_COMMON(0, &op_common);

	if (op_common.version != USBIP_VERSION) {
		fprintf(stderr,"version mismatch, %d %d", op_common.version, USBIP_VERSION);
		goto err;
	}

	switch(*code) {
		case OP_UNSPEC:
			break;
		default:
			if (op_common.code != *code) {
				//info("unexpected pdu %d for %d", op_common.code, *code);
				goto err;
			}
	}

	if (op_common.status != ST_OK) {
		//info("request failed at peer, %d", op_common.status);
		goto err;
	}

	*code = op_common.code;

	return 0;
err:
	return -1;
}

static ssize_t usbip_recv(int sockfd, void *buff, size_t bufflen)
{
	return usbip_xmit(sockfd, buff, bufflen, 0);
}

static void pack_uint32_t(int pack, uint32_t *num)
{
	uint32_t i;

	if (pack)
		i = htonl(*num);
	else
		i = ntohl(*num);

	*num = i;
}

static void pack_usb_device(int pack, struct usb_device *udev)
{
	pack_uint32_t(pack, &udev->busnum);
	pack_uint32_t(pack, &udev->devnum);
	pack_uint32_t(pack, &udev->speed );

	pack_uint16_t(pack, &udev->idVendor );
	pack_uint16_t(pack, &udev->idProduct);
	pack_uint16_t(pack, &udev->bcdDevice);
}

static void usbip_names_get_product(char *buff, size_t size, uint16_t vendor, uint16_t product)
{
	const char *prod, *vend;

	prod = names_product(vendor, product);
	if (!prod)
		prod = "unknown product";


	vend = names_vendor(vendor);
	if (!vend)
		vend = "unknown vendor";

	snprintf(buff, size, "%s : %s (%04x:%04x)", vend, prod, vendor, product);
}

static void usbip_names_get_class(char *buff, size_t size, uint8_t clas, uint8_t subclass, uint8_t protocol)
{
	const char *c, *s, *p;

	if (clas == 0 && subclass == 0 && protocol == 0) {
		snprintf(buff, size, "(Defined at Interface level) (%02x/%02x/%02x)", clas, subclass, protocol);
		return;
	}

	p = names_protocol(clas, subclass, protocol);
	if (!p)
		p = "unknown protocol";

	s = names_subclass(clas, subclass);
	if (!s)
		s = "unknown subclass";

	c = names_class(clas);
	if (!c)
		c = "unknown class";

	snprintf(buff, size, "%s / %s / %s (%02x/%02x/%02x)", c, s, p, clas, subclass, protocol);
}

static void pack_usb_interface(int pack, struct usb_interface *udev)
{
	UNUSED(pack);
	UNUSED(udev);
	/* uint8_t members need nothing */
}

static ssize_t usbip_send(int sockfd, void *buff, size_t bufflen)
{
	return usbip_xmit(sockfd, buff, bufflen, 1);
}

static int import_device(int sockfd, struct usb_device *udev, struct usb_interface *uinf0, HANDLE *devfd)
{
	HANDLE fd;
	int port, ret;

	fd = usbip_vbus_open();
	if (INVALID_HANDLE_VALUE == fd) {
		fprintf(stderr,"open vbus driver");
		return -1;
	}

	port = usbip_vbus_get_free_port(fd);
	if (port <= 0) {
		fprintf(stderr,"no free port");
		CloseHandle(fd);
		return -1;
	}

	fprintf(stdout,"call from attch here\n");
	ret = usbip_vbus_attach_device(fd, port, udev, uinf0);
	fprintf(stdout,"return from attch here\n");

	if (ret < 0) {
		fprintf(stderr,"import device");
		CloseHandle(fd);
		return -1;
	}
	fprintf(stdout,"devfd:%p\n",devfd);
	*devfd=fd;

	return port;
}

static void __cdecl signalhandler(int signal)
{
//	signalflag=1;
	return;
}

// UPRAV fukcia preposielajúca data
int dev_read_async(HANDLE devfd, SOCKET sockfd, OVERLAPPED *ov)
{
	int ret, x;
	unsigned long len;

	do {
		len=0;
		ret = ReadFile(devfd, dev_read_buf, BIG_SIZE, &len, ov);
		if(!ret &&  (x=GetLastError())!=ERROR_IO_PENDING) {
			fprintf(stderr,"read:%d x:%d\n",ret, x);
			return -1;
		}
		if(!ret)
			return 0;
		ret = write_to_sock(dev_read_buf, len, sockfd);
		if(ret<0)
			return -1;
	} while(1);
}

int sock_read_async(SOCKET sockfd, HANDLE devfd, OVERLAPPED *ov_sock, OVERLAPPED *ov_dev)
{
	int ret, err=0;
	unsigned long len;
	do {
		ret = ReadFile((HANDLE)sockfd,  sock_read_buf,
			sizeof(struct usbip_header), &len, ov_sock);
		if (!ret)
			err=GetLastError();

		if(err==ERROR_IO_PENDING)
			return 0;

		if(err) {
			fprintf(stderr,"read:%d err:%d\n",ret, err);
			return -1;
		}

		if (len!=sizeof(struct usbip_header))
		{
			err=GetLastError();
			fprintf(stderr,"incomplete header %d %d\n",ret,err);
		}

		//dbg_file("Bytes read from socket synchronously: %d\n",len);
		ret = write_to_dev(sock_read_buf, BIG_SIZE, len,
				sockfd, devfd, ov_dev);
		if(ret<0)
			return -1;
	}while(1);
}

int dev_read_completed(HANDLE devfd, SOCKET sockfd, OVERLAPPED *ov)
{
	int ret;
	unsigned long len;
	ret = GetOverlappedResult(devfd, ov, &len, FALSE);
	if(!ret){
		fprintf(stderr,"get overlapping failed: %ld", GetLastError());
		return -1;
	}
	ret = write_to_sock(dev_read_buf, len, sockfd);
	if(ret<0)
		return -1;
	return dev_read_async(devfd, sockfd, ov);
}

//zápis do soketu? ............................................
int write_to_sock(char *buf, int len, SOCKET sockfd)
{
	struct usbip_header *u;
	int ret;
	unsigned long out_len, iso_len;

	u=(struct usbip_header *)buf;

	if(len<sizeof(*u)){
		fprintf(stderr,"read dev len: %d\n", len);
		return -1;
	}
	if(!u->base.direction)
		out_len=ntohl(u->u.cmd_submit.transfer_buffer_length);
	else
		out_len=0;
	if(u->u.cmd_submit.number_of_packets)
		iso_len=sizeof(struct usbip_iso_packet_descriptor)*
			ntohl(u->u.cmd_submit.number_of_packets);
	else
		iso_len=0;
	if(len!= sizeof(*u) + out_len + iso_len){
		fprintf(stderr,"read dev len:%d out_len:%ld"
				    "iso_len: %ld\n",
			len, out_len, iso_len);
		return -1;
	}
	if(!u->base.direction&&!record_out(u->base.seqnum)){
		fprintf(stderr,"out q full");
		return -1;
	}
//	dbg_file("send seq:%d\r", ntohl(u->base.seqnum));
//	fprintf(stderr,"Send sequence: %d\n",  ntohl(u->base.seqnum));



	ret=usbip_send(sockfd, buf, len);
	if(ret!=len){
		fprintf(stderr,"send sock len:%d, ret:%d\n", len, ret);
		return -1;
	}
	#ifdef DEBUG
	{
		struct usbip_header cu;
		memcpy(&cu,u,sizeof(struct usbip_header));
		usbip_header_correct_endian(&cu,0);
		usbip_dump_header(&cu);
	}
	#endif
	return 0;
}

int sock_read_completed(SOCKET sockfd, HANDLE devfd, OVERLAPPED *ov_sock, OVERLAPPED *ov_dev)
{

	int ret;
	unsigned long len;
	ret = GetOverlappedResult((HANDLE)sockfd, ov_sock, &len, FALSE);
	if(!ret){
		fprintf(stderr,"get overlapping failed: %ld", GetLastError());
		return -1;
	}
//	dbg_file("Bytes read from socket asynchronously: %d\n",len);
	ret = write_to_dev(sock_read_buf, BIG_SIZE, len, sockfd, devfd, ov_dev);
	if(ret<0)
		return -1;
	return sock_read_async(sockfd, devfd, ov_sock, ov_dev);
}

static void pack_uint16_t(int pack, uint16_t *num)
{
	uint16_t i;

	if (pack)
		i = htons(*num);
	else
		i = ntohs(*num);

	*num = i;
}

static ssize_t usbip_xmit(int sockfd, void *buff, size_t bufflen, int sending)
{
	ssize_t total = 0;

	if (!bufflen)
		return 0;

	do {
		ssize_t nbytes;

		if (sending)
			nbytes = send(sockfd, (const char *)buff, bufflen, 0);
		else
			nbytes = recv(sockfd, (char *)buff, bufflen, 0);

		if (nbytes <= 0) {
			LPVOID lpMsgBuf;

			// Get error information.
			int err = WSAGetLastError();

			if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL)) {
				fprintf(stderr, "[usbip_xmit] recv() returned %d - error %d: %s\n", nbytes, err, lpMsgBuf);
				LocalFree(lpMsgBuf);
			} else {
				fprintf(stderr, "[usbip_xmit] recv() returned %d - error %d\n", nbytes, err);
			}

			return -1;
		}

		buff	= (void *) ((intptr_t) buff + nbytes);
		bufflen	-= nbytes;
		total	+= nbytes;

	} while (bufflen > 0);


	return total;
}

static HANDLE usbip_vbus_open(void)
{
	char buf[256];
	if(NULL==usbip_vbus_dev_node_name(buf, sizeof(buf)))
		return INVALID_HANDLE_VALUE;
	return	CreateFile(buf,
			GENERIC_READ|GENERIC_WRITE,
			  0,
			  NULL,
			  OPEN_EXISTING,
			  FILE_FLAG_OVERLAPPED,
			  NULL);
}

static int usbip_vbus_get_free_port(HANDLE fd)
{
	int i;
	char buf[128];
	if(usbip_vbus_get_ports_status(fd, buf, sizeof(buf)))
		return -1;
	for(i=1;i<sizeof(buf);i++){
		if(!buf[i])
			return i;
	}
	return -1;
}

static int usbip_vbus_attach_device(HANDLE fd, int port, struct usb_device *udev, struct usb_interface *uinf0)
{
	int ret;
	ioctl_usbvbus_plugin  plugin;
	unsigned long unused;

	plugin.devid  = ((udev->busnum << 16)|udev->devnum);
	plugin.vendor = udev->idVendor;
	plugin.product = udev->idProduct;
	plugin.version = udev->bcdDevice;
	plugin.speed = udev->speed;
	plugin.inum = udev->bNumInterfaces;
	plugin.int0_class = uinf0->bInterfaceClass;
	plugin.int0_subclass = uinf0->bInterfaceSubClass;
	plugin.int0_protocol = uinf0->bInterfaceProtocol;
	plugin.addr = port;

	ret = DeviceIoControl(fd, IOCTL_USBVBUS_PLUGIN_HARDWARE,
				&plugin, sizeof(plugin), NULL, 0, &unused, NULL);
	if(ret)
		return 0;
	return -1;
}

int write_to_dev(char * buf, int buf_len, int len, SOCKET sockfd, HANDLE devfd, OVERLAPPED *ov)
{
	int ret;
	unsigned long out=0, in_len, iso_len;
	struct usbip_header * u = (struct usbip_header *)buf;

	if(len!=sizeof(*u)){
		fprintf(stderr,"read from sock ret %d not equal a usbip_header", len);
#ifdef DEBUG
		usbip_dump_buffer(buf,len);
#endif
		return -1;
	}
	if(usbip_header_correct_endian(u, 0)<0)
		return -1;
//	dbg_file("recv seq %d\n", u->base.seqnum);
	if ((u->base.seqnum%100)==0)
		fprintf(stderr,"Receive sequence:    %d\r", u->base.seqnum);

#ifdef DEBUG
	usbip_dump_header(u);
#endif

	if(check_out(htonl(u->base.seqnum)))
		in_len=0;
	else
		in_len=u->u.ret_submit.actual_length;

	iso_len = u->u.ret_submit.number_of_packets
			* sizeof(struct usbip_iso_packet_descriptor);

	if(in_len==0&&iso_len==0){
		ret=WriteFile(devfd, (char *)u, sizeof(*u), &out, ov);
		if(!ret||out!=sizeof(*u)){
			fprintf(stderr,"last error:%ld",GetLastError());
			fprintf(stderr,"out:%ld ret:%d",out,ret);
			fprintf(stderr,"write dev failed");
			return -1;
		}
		return 0;
	}
	len = sizeof(*u) + in_len + iso_len;
	if(len>buf_len){
		fprintf(stderr,"too big len %d %ld %ld", len, in_len,iso_len);
		return -1;
	}
	ret=usbip_recv(sockfd, buf+sizeof(*u),
		in_len+iso_len);
	if(ret != in_len + iso_len){
		fprintf(stderr,"recv from sock failed %d %ld",
				ret,
				in_len + iso_len);
		return -1;
	}

	if(iso_len)
		fix_iso_desc_endian(sock_read_buf+sizeof(*u)+in_len,
					u->u.ret_submit.number_of_packets);
	ret=WriteFile(devfd, buf, len, &out, ov);
	if(!ret||out!=len){
		fprintf(stderr,"last error:%ld\n",GetLastError());
		fprintf(stderr,"out:%ld ret:%d len:%d\n",out,ret,len);
		fprintf(stderr,"write dev failed");
		return -1;
	}
	return 0;
}

int record_out(long num)
{
	int i;
	for(i=0;i<OUT_Q_LEN;i++){
		if(out_q_seqnum_array[i])
			continue;
		out_q_seqnum_array[i]=num;
		return 1;
	}
	return 0;
}

static char * usbip_vbus_dev_node_name(char *buf, unsigned long buf_len)
{
	HDEVINFO dev_info;
	SP_DEVICE_INTERFACE_DATA dev_interface_data;
	PSP_DEVICE_INTERFACE_DETAIL_DATA dev_interface_detail = NULL;
	SP_DEVINFO_DATA dev_info_data;
	unsigned long len;
	char *ret=NULL;
	int memberIndex = 0, rc = 1;
	char hardwareID[256] = {0};

	// Get devices info.
	dev_info = SetupDiGetClassDevs(
		(LPGUID) &GUID_DEVINTERFACE_BUSENUM_USBIP, /* ClassGuid */
		NULL,	/* Enumerator */
		NULL,	/* hwndParent */
		DIGCF_PRESENT|DIGCF_DEVICEINTERFACE /* Flags */
	);
	if (INVALID_HANDLE_VALUE == dev_info) {
		fprintf(stderr,"SetupDiGetClassDevs failed: %ld\n", GetLastError());
		return NULL;
	}

	// Prepare some structures.
	dev_interface_data.cbSize = sizeof (dev_interface_data);
	dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);

	// Loop reading information from the devices until we get some error.
	while (rc) {
		// Get device info data.
		rc = SetupDiEnumDeviceInfo(
			dev_info,		/* DeviceInfoSet */
			memberIndex,	/* MemberIndex */
			&dev_info_data	/* DeviceInfoData */
		);
		if (!rc) {
			if (GetLastError() == ERROR_NO_MORE_ITEMS) {
				// No more items. Leave.
				memberIndex = -1;
				break;
			} else {
				// Something else...
				fprintf(stderr,"error getting device information\n");
				goto end;
			}
		}

		// Get hardware ID.
		rc = SetupDiGetDeviceRegistryProperty(
			dev_info,			/* DeviceInfoSet */
			&dev_info_data,		/* DeviceInfoData */
			SPDRP_HARDWAREID,	/* Property */
			0L,					/* PropertyRegDataType */
			(PBYTE)hardwareID,	/* PropertyBuffer */
			sizeof(hardwareID),	/* PropertyBufferSize */
			0L					/* RequiredSize */
		);
		if (!rc) {
			// We got some error reading the hardware id. I'm pretty sure this isn't supposed
			// to happen, but let's continue anyway.
			memberIndex++;
			continue;
		}

		// Check if we got the correct device.
		if (strcmp(hardwareID, "root\\usbipenum") != 0) {
			// Wrong hardware ID. Get the next one.
			memberIndex++;
			continue;
		} else {
			// Got it!
			break;
		}
	}

	// Get device interfaces.
	rc = SetupDiEnumDeviceInterfaces(
		dev_info,									/* DeviceInfoSet */
		NULL,										/* DeviceInfoData */
		(LPGUID) &GUID_DEVINTERFACE_BUSENUM_USBIP,	/* InterfaceClassGuid */
		memberIndex,								/* MemberIndex */
		&dev_interface_data							/* DeviceInterfaceData */
	);
	if (!rc) {
		// No more items here isn't supposed to happen since we checked that on the
		// SetupDiEnumDeviceInfo, but there's no harm checking again.
		if (ERROR_NO_MORE_ITEMS == GetLastError()) {
			fprintf(stderr,"usbvbus interface is not registered\n");
		} else {
			fprintf(stderr,"unknown error when get interface_data\n");
		}
		goto end;
	}

	// Get required length for dev_interface_detail.
	rc = SetupDiGetDeviceInterfaceDetail(
		dev_info,				/* DeviceInfoSet */
		&dev_interface_data,	/* DeviceInterfaceData */
		NULL,					/* DeviceInterfaceDetailData */
		0,						/* DeviceInterfaceDetailDataSize */
		&len,					/* RequiredSize */
		NULL					/* DeviceInfoData */
	);
	if (ERROR_INSUFFICIENT_BUFFER != GetLastError()) {
		fprintf(stderr,"Error in SetupDiGetDeviceInterfaceDetail: %ld\n", GetLastError());
		goto end;
	}

	// Allocate the required memory and set the cbSize.
	dev_interface_detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(len);
	if (NULL == dev_interface_detail) {
		fprintf(stderr,"can't malloc %lu size memoery", len);
		goto end;
	}
	dev_interface_detail->cbSize = sizeof (*dev_interface_detail);

	// Try to get device details.
	rc = SetupDiGetDeviceInterfaceDetail(
		dev_info,				/* DeviceInfoSet */
		&dev_interface_data,	/* DeviceInterfaceData */
		dev_interface_detail,	/* DeviceInterfaceDetailData */
		len,					/* DeviceInterfaceDetailDataSize */
		&len,					/* RequiredSize */
		NULL					/* DeviceInfoData */
	);
	if (!rc) {
		// Errors.
		fprintf(stderr,"Error in SetupDiGetDeviceInterfaceDetail\n");
		goto end;
	}

	// Copy the device path to the buffer.
	len = _snprintf(buf, buf_len, "%s", dev_interface_detail->DevicePath);
	if (len>=buf_len) {
		goto end;
	}
	ret = buf;

end:
	// Free the detail memory and destroy the device information list.
	if (dev_interface_detail) {
		free(dev_interface_detail);
	}
	SetupDiDestroyDeviceInfoList(dev_info);

	return ret;
}

static int usbip_vbus_get_ports_status(HANDLE fd, char *buf, int l)
{
	int ret;
	unsigned long len;
	ioctl_usbvbus_get_ports_status * st=(ioctl_usbvbus_get_ports_status *)buf;

	if(l!=sizeof(*st))
		return -1;

	ret = DeviceIoControl(fd, IOCTL_USBVBUS_GET_PORTS_STATUS,
				NULL, 0, st, sizeof(*st), &len, NULL);
	if(ret&&len==sizeof(*st))
		return 0;
	else
		return -1;
}

static int usbip_header_correct_endian(struct usbip_header *pdu, int send)
{
	unsigned int cmd = 0;

	if (send)
		cmd = pdu->base.command;

	correct_endian_basic(&pdu->base, send);

	if (!send)
		cmd = pdu->base.command;

	switch (cmd) {
		case USBIP_CMD_SUBMIT:
			correct_endian_cmd_submit(&pdu->u.cmd_submit);
			break;
		case USBIP_RET_SUBMIT:
			correct_endian_ret_submit(&pdu->u.ret_submit);
			break;
		default:
			/* NOTREACHED */
			fprintf(stderr,"unknown command in pdu header: %d", cmd);
			return -1;
			//BUG();
	}
	return 0;
}

int check_out(unsigned long num)
{
	int i;
	for(i=0;i<OUT_Q_LEN;i++){
		if(out_q_seqnum_array[i]!=num)
			continue;
		out_q_seqnum_array[i]=0;
		return 1;
	}
	return 0;
}

static void fix_iso_desc_endian(char *buf, int num)
{
	struct usbip_iso_packet_descriptor * ip_desc;
	int i;
	int all=0;
	ip_desc = (struct usbip_iso_packet_descriptor *) buf;
	for(i=0;i<num;i++){
		ip_desc->offset = ntohl(ip_desc->offset);
		ip_desc->status = ntohl(ip_desc->status);
		ip_desc->length = ntohl(ip_desc->length);
		ip_desc->actual_length = ntohl(ip_desc->actual_length);
		all+=ip_desc->actual_length;
		ip_desc++;
	}
}

static void correct_endian_basic(struct usbip_header_basic *base, int send)
{
	if (send) {
		base->command	= htonl(base->command);
		base->seqnum	= htonl(base->seqnum);
		base->devid	= htonl(base->devid);
		base->direction	= htonl(base->direction);
		base->ep	= htonl(base->ep);
	} else {
		base->command	= ntohl(base->command);
		base->seqnum	= ntohl(base->seqnum);
		base->devid	= ntohl(base->devid);
		base->direction	= ntohl(base->direction);
		base->ep	= ntohl(base->ep);
	}
}

static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu)
{
	pdu->transfer_flags	= ntohl(pdu->transfer_flags);
	pdu->transfer_buffer_length = ntohl(pdu->transfer_buffer_length);
	pdu->start_frame = ntohl(pdu->start_frame);
	pdu->number_of_packets = ntohl(pdu->number_of_packets);
	pdu->interval = ntohl(pdu->interval);
}

static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu)
{
	pdu->status	= ntohl(pdu->status);
	pdu->actual_length = ntohl(pdu->actual_length);
	pdu->start_frame = ntohl(pdu->start_frame);
	pdu->number_of_packets = ntohl(pdu->number_of_packets);
	pdu->error_count = ntohl(pdu->error_count);
}

static int init_socket()
{
    WSADATA wsaData;
    int err;

    err = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (err != 0) {
        fprintf(stderr,"WSAStartup failed with error: %d\n", err);
        return -1;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        fprintf(stderr,"Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return -1;
    }
	return 0;
}
		// TODO: Add your methods for this class here.
	};
}
