Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Question about USB, windows 7 and PIC controller

Status
Not open for further replies.

cipi-cips

Member level 4
Joined
Jun 30, 2008
Messages
76
Helped
2
Reputation
4
Reaction score
1
Trophy points
1,288
Activity points
1,867
Hello

I have heard that windows 7 is using virtual addressing for usb peripherals i.e. it doesnt communicate direct with connected hardware, how to solve that ? Is the HID subclass answare to my question ?

Thx
 

Implementing a Communications Device Class (CDC) with Virtual COM Port Emulation would probably be the most straightforward method.

No special/custom drivers are required and in the case of Windows only the modification of the standard INF file is needed.

The PC side application simply accesses the Virtual COM Port, similarly to accessing a standard Serial COM Port.

AN956: Migrating Applications to USB from RS-232 UART with Minimal Impact on PC Software

Source Code for Appnote AN956

USB CDC Specification

The Communication Device Class (CDC) specification
defines many communication models, including serial
emulation. All references to the CDC specification in
this document refer to version 1.1. The Microsoft
Windows driver, usbser.sys, conforms to this specification.
Therefore, the embedded device must also be
designed to conform with this specification in
order to utilize this existing Windows driver.

The HID as well as other specifications can also be implemented, however most require significant driver or PC application development.


BigDog
 

Thx for answer, I have found some programing code for USB both firmware and software. So does anyone know where in the code is written that direct communication with hardware (PIC micrcontroller) since windows 7 is using virtual addressing.

Firmware code

Code:
// USBFunctions.c

// includes ///////////////////////////////////////////////////////////////////////////////////////
#include<p18f4550.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<limits.h>
#include"USBFunctions.h"

// global variables ///////////////////////////////////////////////////////////////////////////////
#pragma udata USB_BDT = 0x400			// see linker script, usb4: 0x400 - 0x4FF (256 byte)
volatile BDT_ENTRY g_buffDescTable[4];								// 4 bytes each, 16 bytes total
volatile CTRL_TRF_SETUP g_ctrlTrfSetupPkt;							// 				 8 bytes
volatile BYTE g_ctrlTrfData[USB_EP0_BUFF_SIZE];						// 1 byte each,    8 bytes total
#pragma udata					// end of #pragma udata USB_BDT = 0x400		      32 bytes total

#pragma udata USB_VARS
BYTE g_fromHostToDeviceBuffer[65];
BYTE g_fromDeviceToHostBuffer[65];
#pragma udata					// end of #pragma udata USB_VARS

OUT_PIPE g_outPipe;
IN_PIPE  g_inPipe;

BYTE g_USBDeviceState = DETACHED_STATE;
BYTE g_USBDeviceState;
BYTE g_controlTransferState;
BYTE g_USBActiveConfiguration;
BYTE g_shortPacketStatus;

#pragma romdata										// USB device descriptors
	// device descriptor
rom USB_DEVICE_DESCRIPTOR g_userDeviceDescriptor = {	0x12,
														USB_DESCRIPTOR_DEVICE,
														0x0200,
														0x00,
														0x00,
														0x00,
														USB_EP0_BUFF_SIZE,
														0x04D8,					// Vendor ID (VID)
														0x003F,					// Product ID (PID)
														0x0002,
														0x01,
														0x02,
														0x00,
														0x01	};
	// config 1 descriptor
rom BYTE g_configDescriptor1[] = {	0x09,					// config descriptor
									USB_DESCRIPTOR_CONFIGURATION,
									0x29,
									0x00,
									1,
									1,
									0,
									_DEFAULT | _SELF,
									50,
									
									0x09,					// interface descriptor
									USB_DESCRIPTOR_INTERFACE,
									0,
									0,
									2,
									HID_INTF,
									0,
									0,
									0,
									
									0x09,					// HID descriptor
									HID_DESCRIPTOR,
									0x11,
									0x01,
									0x00,
									HID_NUM_OF_DSC,
									REPORT_DESCRIPTOR,
									HID_RPT01_SIZE, 0x00,
									
									0x07,					// endpoint descriptor
									USB_DESCRIPTOR_ENDPOINT,
									1 | _EP_IN,
									_INT,
									0x40,
									0x00,
									0x01,
									
									0x07,					// endpoint descriptor
									USB_DESCRIPTOR_ENDPOINT,
									1 | _EP_OUT,
									_INT,
									0x40,
									0x00,
									0x01	};

	// language code string descriptor
rom struct{BYTE bLength; BYTE bDscType; WORD string[1];} g_stringDescript000 = { sizeof(g_stringDescript000), USB_DESCRIPTOR_STRING, { 0x0409 } };

	// mfg. string descriptor
rom struct{BYTE bLength; BYTE bDscType; WORD string[25];} g_stringDescript001 = {	sizeof(g_stringDescript001),
																				USB_DESCRIPTOR_STRING,
																				{'M','i','c','r','o','c','h','i','p',' ','T','e','c','h','n','o','l','o','g','y',' ','I','n','c','.'} };

	// product string descriptor
rom struct{BYTE bLength; BYTE bDscType; WORD string[22];} g_stringDescript002 = {	sizeof(g_stringDescript002),
																				USB_DESCRIPTOR_STRING,
																				{'S','i','m','p','l','e',' ','H','I','D',' ','D','e','v','i','c','e',' ','D','e','m','o'} };

rom struct{BYTE report[HID_RPT01_SIZE];} g_HIDReport01 = { {	0x06, 0x00, 0xFF,
															0x09, 0x01,
															0xA1, 0x01,
															0x19, 0x01,
															0x29, 0x40,
															0x15, 0x00,
															0x26, 0xFF, 0x00,
															0x75, 0x08,
															0x95, 0x40,
															0x81, 0x02,
															0x19, 0x01,
															0x29, 0x40,
															0x91, 0x02,
															0xC0 } };

	// array of config descriptors
rom BYTE *rom g_userConfigDescriptorPtr[] = { (rom BYTE *rom)&g_configDescriptor1 };

	// array of string descriptors
rom BYTE *rom g_USBStringDescPtr[] = {	(rom BYTE *rom)&g_stringDescript000,
										(rom BYTE *rom)&g_stringDescript001,
										(rom BYTE *rom)&g_stringDescript002		};
#pragma udata 			// end of #pragma romdata for descriptor global variables

#pragma code
///////////////////////////////////////////////////////////////////////////////////////////////////
void USBInit(void) {
	BYTE i;
	
	UEIR = 0x00;		// clear USB error interrupt status register
	UIR = 0x00;			// clear USB interrupt status register
	
						// UEIE -> USB error interrupt enable register
	UEIEbits.BTSEE   = 1;	// bit stuff error interrupt enabled
	UEIEbits.BTOEE   = 1;	// bus turnaround time-out error interrupt enabled
	UEIEbits.DFN8EE  = 1;	// data field size error interrupt enabled
	UEIEbits.CRC16EE = 1;	// CRC16 failure interrupt enabled
	UEIEbits.CRC5EE  = 1;	// CRC5 host error interrupt enabled
	UEIEbits.PIDEE   = 1;	// PID check failure interrupt enabled
	
							// UIE -> USB interrupt enable register
	UIEbits.SOFIE   = 1;	// start of frame token interrupt enabled
	UIEbits.STALLIE = 1;	// STALL interrupt enabled
	UIEbits.IDLEIE  = 1;	// idle detect interrupt enabled
	UIEbits.TRNIE   = 1;	// transaction interrupt enabled
	UIEbits.ACTVIE  = 0;	// bus activity detect interrupt disabled
	UIEbits.UERRIE  = 1;	// USB error interrupt enabled
	UIEbits.URSTIE  = 1;	// USB reset interrupt enabled
	
	UCONbits.PPBRST = 1;	// reset ping-pong buffer pointers to even buffer descriptor banks
	UCONbits.PPBRST = 0;	// ping-pong buffer pointers not being reset
	
	UADDR = 0x00;			// set USB address register
	
	for(i=0; i<(sizeof(g_buffDescTable) / sizeof(BDT_ENTRY)); i++) {		// clear all buffer descriptor table entries
		g_buffDescTable[i].STAT.STATVal = 0x00;
		g_buffDescTable[i].CNT = 0x00;
		g_buffDescTable[i].ADR = 0x0000;
	}
	
								// USB endpoint 0 configuration
	UEP0bits.EPHSHK   = 1;		// endpoint 0 handshake enabled
	UEP0bits.EPCONDIS = 0;		// enable endpoint 0 for control (SETUP) transfers, IN & OUT transfers also allowed
	UEP0bits.EPOUTEN  = 1;		// endpoint 0 output enabled
	UEP0bits.EPINEN   = 1;		// endpoint 0 input enabled
	UEP0bits.EPSTALL  = 0;		// endpoint 0 has not issued any STALL packets
	
												// flush any pending transactions
	while(UIRbits.TRNIF == 1) {					// while processing of pending transaction is complete . . .
		UIRbits.TRNIF = 0;						// set processing of pending transaction to not complete
	}
	
	g_inPipe.busy = 0;			// clear all internal pipe information
	g_outPipe.busy = 0;
	g_outPipe.wCount = 0;
	
	UCONbits.PKTDIS = 0;					// SIE token & packet processing enabled
	
	g_USBActiveConfiguration = 0;				// clear active configuration
	g_USBDeviceState = DETACHED_STATE;			// init state to detached
	g_fromHostToDeviceBuffer[1] = 0x00;
	g_fromHostToDeviceBuffer[2] = 0x00;
	g_fromHostToDeviceBuffer[3] = 0x32;
	g_fromHostToDeviceBuffer[4] = 0x8E;
	g_fromHostToDeviceBuffer[5] = 0x6E;
	g_fromHostToDeviceBuffer[6] = 0x32;

g_fromDeviceToHostBuffer[1] = 0x00;
g_fromDeviceToHostBuffer[2] = 0x00;
g_fromDeviceToHostBuffer[3] = 0x00;
g_fromDeviceToHostBuffer[4] = 0x00;
g_fromDeviceToHostBuffer[5] = 0x00;
g_fromDeviceToHostBuffer[6] = 0x00;
g_fromDeviceToHostBuffer[7] = 0x00;
	for(i=7;i<65;i++) {								// init buffer arrays to zero
		g_fromHostToDeviceBuffer[i] = 0x00;
		g_fromDeviceToHostBuffer[i] = 0x00;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBTasks(void) {
	BYTE i;
	
	if(g_USBDeviceState == DETACHED_STATE) {	// if we are in detached state
		UCON = 0x00;							// disable & detach from bus
		UIE  = 0x00;							// disable all UIE interrupts
		while(UCONbits.USBEN == 0) {
			UCONbits.USBEN = 1;					// enable & attach to bus
		}
		g_USBDeviceState = ATTACHED_STATE;		// update state variable
		
								// USB config register
		UCFGbits.UTEYE  = 0;	// eye pattern test disabled
		UCFGbits.UOEMON = 0;	// USB output enable monitor enable bit
		UCFGbits.UPUEN  = 1;	// on chip pull-up resistors enabled
		UCFGbits.UTRDIS = 0;	// on chip transceiver active
		UCFGbits.FSEN   = 1;	// full speed clock (48 MHz)
		UCFGbits.PPB1   = 0;	// no ping pong
		UCFGbits.PPB0   = 0;	// no ping pong
	}
	
	if(g_USBDeviceState == ATTACHED_STATE) {
						/*  After enabling the USB module, it takes some time for the voltage on the D+ or D- lines to rist high enough
							to get out of the single-ended zero condition.  The USB reset interrupt should not be unmasked until the
							SE0 condition is cleared, this helps prevent the firmware from misinterpreting a single-ended zero condition
							as a USB bus reset from the USB host */
		if(UCONbits.SE0 == 0) {					// if not a single-ended zero condition . . .
			UIR = 0x00;							// clear interrupt register
			UIE = 0x00;							// disable all UIE interrupts
			UIEbits.URSTIE = 1;					// USB reset interrupt enabled
			UIEbits.IDLEIE = 1;					// idle detect interrupt enabled
			g_USBDeviceState = POWERED_STATE;	// update state
		}
	}
	
	if(UIRbits.ACTVIF &&		// if D+ / D- activity detected
	   UIEbits.ACTVIE) {		// and bus activity detect interrupt enabled . . .
		USBWakeFromSuspend();
	}
	
	if(UCONbits.SUSPND == 1) {
		return;
	}
	
	if(UIRbits.URSTIF &&						// if valid USB reset occurred
	   UIEbits.URSTIE) {						// USB reset interrupt enabled
		USBInit();								// call init again
		g_USBDeviceState = DEFAULT_STATE;		// set state variable
		g_buffDescTable[0].ADR = (BYTE*)&g_ctrlTrfSetupPkt;
		g_buffDescTable[0].CNT = USB_EP0_BUFF_SIZE;
		g_buffDescTable[0].STAT.DTS    = 0;		// data toggle synch bit
		g_buffDescTable[0].STAT.KEN    = 0;		// keep enable bit
		g_buffDescTable[0].STAT.INCDIS = 0;		// address increment disable bit
		g_buffDescTable[0].STAT.DTSEN  = 1;		// data toggle synch enable bit
		g_buffDescTable[0].STAT.BSTALL = 1;		// buffer stall enable bit
		g_buffDescTable[0].STAT.BC9    = 0;		// byte count 9 bit
		g_buffDescTable[0].STAT.BC8    = 0;		// byte count 8 bit
		g_buffDescTable[0].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)		
	}
	
	if(UIRbits.IDLEIF &&			// if idle detected
	   UIEbits.IDLEIE) {			// and idle detect interrupt enabled . . .
		USBSuspend();
	}
	
	if(UIRbits.SOFIF &&				// if start of frame token received by SIE
		UIEbits.SOFIE) {			// and start of frame token interrupt enabled . . .
		UIRbits.SOFIF = 0;			// clear start of frame token bit
	}
	
	if(UIRbits.STALLIF &&			// if stall handshake sent by SIE
		UIEbits.STALLIE) {			// and stall interrupt enabled . . .
		USBStallHandler();
	}
	
	if(UIRbits.UERRIF &&			// if unmasked error has occurred
		UIEbits.UERRIE) {			// and error interrupt enabled . . .
		UEIR = 0x00;				// clear USB error interrupt register
	}
	
	if(g_USBDeviceState < DEFAULT_STATE) return;		// bail if we have not at least reached default state
	
	if(UIEbits.TRNIE) {				// if transaction interrupts enabled
		for(i=0;i<4;i++) {				// for each entry in the USTAT FIFO . . .
			if(UIRbits.TRNIF) {			// if processing of pending transaction is complete . . .
				if(USTATbits.ENDP3 == 0 &&
				   USTATbits.ENDP2 == 0 &&
				   USTATbits.ENDP1 == 0 &&
				   USTATbits.ENDP0 == 0) {			// if last transfer was EP0 . . .
					USBEP0ControlTransfer();		// this leads to g_USBDeviceState = CONFIGURED_STATE
				}	
				UIRbits.TRNIF = 0;					// clear transaction complete interrupt bit, causes USTAT FIFO to advance
			} else {					// else jump out of for loop
				break;					// this kicks out of the for loop
			}
		}
		// break keyword takes us to here
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBWakeFromSuspend(void) {
	UCONbits.SUSPND = 0;			// clear suspend bit (wake from suspend)
	UIEbits.ACTVIE = 0;				// disable bus activity detect interrupt
	while(UIRbits.ACTVIF) {			// while activity on D+/D- lines was detected . . .
		UIRbits.ACTVIF = 0;			// clear bus activity detect interrupt flag bit
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBSuspend(void) {
	UIEbits.ACTVIE = 1;			// set bus activity detect interrupt enable bit
	UIRbits.IDLEIF = 0;			// clear idle detect interrupt bit
	UCONbits.SUSPND = 1;		// set USB suspend bit (enter suspend mode)
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBStallHandler(void) {
	if(UEP0bits.EPSTALL == 1) {				// if endpoint 0 has issued one or more stall packets . . .
		if((g_buffDescTable[0].STAT.UOWN   == 1) && (g_buffDescTable[1].STAT.UOWN   == 1)) {
			g_buffDescTable[0].STAT.DTS    = 0;		// data toggle synch bit
			g_buffDescTable[0].STAT.KEN    = 0;		// keep enable bit
			g_buffDescTable[0].STAT.INCDIS = 0;		// address increment disable bit
			g_buffDescTable[0].STAT.DTSEN  = 1;		// data toggle synch enable bit
			g_buffDescTable[0].STAT.BSTALL = 1;		// buffer stall enable bit
			g_buffDescTable[0].STAT.BC9    = 0;		// byte count 9 bit
			g_buffDescTable[0].STAT.BC8    = 0;		// byte count 8 bit
			g_buffDescTable[0].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
		}
		UEP0bits.EPSTALL = 0;				// clear endpoint 0 stall indicator bit
	}
	UIRbits.STALLIF = 0;					// clear stall handshake interrupt bit
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBEP0ControlTransfer(void) {
	if(USTATbits.DIR == 0) {				// if last trans was EP0 and OUT or SETUP
		if(g_buffDescTable[0].STAT.PID == SETUP_TOKEN) {
			USBSetupControlTransfer();		// this leads to g_USBDeviceState = CONFIGURED_STATE
		} else {
			USBOutControlTransfer();
		}
	} else if(USTATbits.DIR == 1) {		// if last trans was EP0 and IN
		USBInControlTransfer();
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBSetupControlTransfer(void) {
	g_buffDescTable[1].STAT.STATVal = 0x00;
	g_shortPacketStatus = SHORT_PKT_NOT_USED;
	g_controlTransferState = CTRL_TRF_WAIT_SETUP;
	g_inPipe.wCount = 0;
	g_inPipe.busy = 0;
	USBCheckStandardRequest();				// this leads to g_USBDeviceState = CONFIGURED_STATE
	USBCheckHIDRequest();
	USBFinishControlTransferStuff();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBCheckStandardRequest(void) {
	if(g_ctrlTrfSetupPkt.RequestType != STANDARD) return;
	
	if(g_ctrlTrfSetupPkt.bRequest == SET_ADDRESS) {
		g_inPipe.busy = 1;
		g_USBDeviceState = ADR_PENDING_STATE;
	} else if(g_ctrlTrfSetupPkt.bRequest == GET_DESCRIPTOR) {
		if(g_ctrlTrfSetupPkt.bmRequestType == 0x80) {
			g_inPipe.busy = 1;
			if(g_ctrlTrfSetupPkt.bDescriptorType == USB_DESCRIPTOR_DEVICE) {
				g_inPipe.bRom = (rom BYTE*)&g_userDeviceDescriptor;
				g_inPipe.wCount = sizeof(g_userDeviceDescriptor);
			} else if(g_ctrlTrfSetupPkt.bDescriptorType == USB_DESCRIPTOR_CONFIGURATION) {
				g_inPipe.bRom = *(g_userConfigDescriptorPtr + g_ctrlTrfSetupPkt.bDscIndex);
				g_inPipe.wRom = (rom WORD*)g_inPipe.bRom;
				g_inPipe.wCount = *(g_inPipe.wRom + 1);
			} else if(g_ctrlTrfSetupPkt.bDescriptorType == USB_DESCRIPTOR_STRING) {
				g_inPipe.bRom = *(g_USBStringDescPtr + g_ctrlTrfSetupPkt.bDscIndex);
				g_inPipe.wCount = *g_inPipe.bRom;
			} else {
				g_inPipe.busy = 0;
			}
		}
	} else if(g_ctrlTrfSetupPkt.bRequest == SET_CONFIGURATION) {
		g_inPipe.busy = 1;
		g_USBActiveConfiguration = g_ctrlTrfSetupPkt.bConfigurationValue;
		if(g_ctrlTrfSetupPkt.bConfigurationValue == 0) {
			g_USBDeviceState = ADDRESS_STATE;
		} else {
			g_USBDeviceState = CONFIGURED_STATE;		// this is the only line in the program that changes g_USBDeviceState to CONFIGURED_STATE		
							// enable & configure endpoint 1
			UEP1bits.EPHSHK = 1;		// endpoint 1 handshake enabled
			UEP1bits.EPCONDIS = 1;		// disable endpoint 1 from performing control (i.e. setup) transfers
			UEP1bits.EPOUTEN = 1;		// endpoint 1 output enabled
			UEP1bits.EPINEN = 1;		// endpoint 1 input enabled
			UEP1bits.EPSTALL = 0;		// clear endpoint 1 stall indicator bit
			g_buffDescTable[2].STAT.UOWN = 0;
			g_buffDescTable[2].STAT.DTS  = 1;
			g_buffDescTable[3].STAT.UOWN = 0;
			g_buffDescTable[3].STAT.DTS  = 1;
			
			transferFromHostToDeviceViaEP1((BYTE*)&g_fromHostToDeviceBuffer[1], 64);
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBCheckHIDRequest(void) {
	if(g_ctrlTrfSetupPkt.Recipient != RECIPIENT_INTERFACE) return;
	
	if(g_ctrlTrfSetupPkt.bRequest == GET_DESCRIPTOR) {
		if(g_ctrlTrfSetupPkt.bDescriptorType == HID_DESCRIPTOR) {
			if(g_USBActiveConfiguration == 1) {
				g_inPipe.bRom = (rom BYTE*)&g_configDescriptor1 + 18;
				g_inPipe.wCount = sizeof(USB_HID_DSC) + 3;
				g_inPipe.busy = 1;
			}
		} else if(g_ctrlTrfSetupPkt.bDescriptorType == REPORT_DESCRIPTOR) {
			if(g_USBActiveConfiguration == 1) {
				g_inPipe.bRom = (rom BYTE*)&g_HIDReport01;
				g_inPipe.wCount = sizeof(g_HIDReport01);
				g_inPipe.busy = 1;
			}
		} else if(g_ctrlTrfSetupPkt.bDescriptorType == PHY_DESCRIPTOR) {
			g_inPipe.busy = 1;
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBFinishControlTransferStuff(void) {
	UCONbits.PKTDIS = 0;
	if(g_inPipe.busy == 0) {
		if(g_outPipe.busy == 1) {
			//
		} else {
			g_buffDescTable[0].CNT = USB_EP0_BUFF_SIZE;
			g_buffDescTable[0].ADR = (BYTE*)&g_ctrlTrfSetupPkt;
			g_buffDescTable[0].STAT.DTS    = 0;		// clear data toggle synch bit
			g_buffDescTable[0].STAT.KEN    = 0;		// clear keep enable bit
			g_buffDescTable[0].STAT.INCDIS = 0;		// clear address increment disable bit
			g_buffDescTable[0].STAT.DTSEN  = 1;		// set data toggle synch enable bit
			g_buffDescTable[0].STAT.BSTALL = 1;		// set buffer stall enable bit
			g_buffDescTable[0].STAT.BC9    = 0;		// clear byte count 9 bit
			g_buffDescTable[0].STAT.BC8    = 0;		// clear byte count 8 bit
			g_buffDescTable[0].STAT.UOWN   = 1;		// set UOWN bit (SIE owns buffer)
			
			g_buffDescTable[1].STAT.DTS    = 0;		// clear data toggle synch bit
			g_buffDescTable[1].STAT.KEN    = 0;		// clear keep enable bit
			g_buffDescTable[1].STAT.INCDIS = 0;		// clear address increment disable bit
			g_buffDescTable[1].STAT.DTSEN  = 0;		// clear data toggle synch enable bit
			g_buffDescTable[1].STAT.BSTALL = 1;		// set buffer stall enable bit
			g_buffDescTable[1].STAT.BC9    = 0;		// clear byte count 9 bit
			g_buffDescTable[1].STAT.BC8    = 0;		// clear byte count 8 bit
			g_buffDescTable[1].STAT.UOWN   = 1;		// set UOWN bit (SIE owns buffer)
		}
	} else {
		if(g_outPipe.busy == 0) {
			if(g_ctrlTrfSetupPkt.DataDir == DEV_TO_HOST) {
				if(g_ctrlTrfSetupPkt.wLength < g_inPipe.wCount) {
					g_inPipe.wCount = g_ctrlTrfSetupPkt.wLength;
				}
				USBControlTransferTransmit();
				g_controlTransferState = CTRL_TRF_TX;
				g_buffDescTable[0].CNT = USB_EP0_BUFF_SIZE;
				g_buffDescTable[0].ADR = (BYTE*)&g_ctrlTrfSetupPkt;
				g_buffDescTable[0].STAT.DTS    = 0;		// data toggle synch bit
				g_buffDescTable[0].STAT.KEN    = 0;		// keep enable bit
				g_buffDescTable[0].STAT.INCDIS = 0;		// address increment disable bit
				g_buffDescTable[0].STAT.DTSEN  = 0;		// data toggle synch enable bit
				g_buffDescTable[0].STAT.BSTALL = 0;		// buffer stall enable bit
				g_buffDescTable[0].STAT.BC9    = 0;		// byte count 9 bit
				g_buffDescTable[0].STAT.BC8    = 0;		// byte count 8 bit
				g_buffDescTable[0].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
				
				g_buffDescTable[1].ADR = (BYTE*)&g_ctrlTrfData;
				g_buffDescTable[1].STAT.DTS    = 1;		// data toggle synch bit
				g_buffDescTable[1].STAT.KEN    = 0;		// keep enable bit
				g_buffDescTable[1].STAT.INCDIS = 0;		// address increment disable bit
				g_buffDescTable[1].STAT.DTSEN  = 1;		// data toggle synch enable bit
				g_buffDescTable[1].STAT.BSTALL = 0;		// buffer stall enable bit
				g_buffDescTable[1].STAT.BC9    = 0;		// byte count 9 bit
				g_buffDescTable[1].STAT.BC8    = 0;		// byte count 8 bit
				g_buffDescTable[1].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
				
			} else {
				g_controlTransferState = CTRL_TRF_RX;
				g_buffDescTable[1].CNT = 0;
				g_buffDescTable[1].STAT.DTS    = 1;		// data toggle synch bit
				g_buffDescTable[1].STAT.KEN    = 0;		// keep enable bit
				g_buffDescTable[1].STAT.INCDIS = 0;		// address increment disable bit
				g_buffDescTable[1].STAT.DTSEN  = 1;		// data toggle synch enable bit
				g_buffDescTable[1].STAT.BSTALL = 0;		// buffer stall enable bit
				g_buffDescTable[1].STAT.BC9    = 0;		// byte count 9 bit
				g_buffDescTable[1].STAT.BC8    = 0;		// byte count 8 bit
				g_buffDescTable[1].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
				
				g_buffDescTable[0].CNT = USB_EP0_BUFF_SIZE;
				g_buffDescTable[0].ADR = (BYTE*)&g_ctrlTrfData;		
				g_buffDescTable[0].STAT.DTS    = 1;		// data toggle synch bit
				g_buffDescTable[0].STAT.KEN    = 0;		// keep enable bit
				g_buffDescTable[0].STAT.INCDIS = 0;		// address increment disable bit
				g_buffDescTable[0].STAT.DTSEN  = 1;		// data toggle synch enable bit
				g_buffDescTable[0].STAT.BSTALL = 0;		// buffer stall enable bit
				g_buffDescTable[0].STAT.BC9    = 0;		// byte count 9 bit
				g_buffDescTable[0].STAT.BC8    = 0;		// byte count 8 bit
				g_buffDescTable[0].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
				
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBOutControlTransfer(void) {
	if(g_controlTransferState == CTRL_TRF_RX) {
		USBControlTransferReceive();
	} else {
		USBPrepareForNextSetupTransfer();
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBInControlTransfer(void) {
	BYTE lastDTS;
	
	lastDTS = g_buffDescTable[1].STAT.DTS;
	
	if(g_USBDeviceState == ADR_PENDING_STATE) {
		UADDR = g_ctrlTrfSetupPkt.bDevADR;
		if(UADDR > 0) {
			g_USBDeviceState = ADDRESS_STATE;
		} else {
			g_USBDeviceState = DEFAULT_STATE;
		}
	}
	
	if(g_controlTransferState == CTRL_TRF_TX) {
		g_buffDescTable[1].ADR = (BYTE*)g_ctrlTrfData;
		USBControlTransferTransmit();
		if(g_shortPacketStatus == SHORT_PKT_SENT) {
			g_buffDescTable[1].STAT.DTS    = 0;		// data toggle synch bit
			g_buffDescTable[1].STAT.KEN    = 0;		// keep enable bit
			g_buffDescTable[1].STAT.INCDIS = 0;		// address increment disable bit
			g_buffDescTable[1].STAT.DTSEN  = 0;		// data toggle synch enable bit
			g_buffDescTable[1].STAT.BSTALL = 1;		// buffer stall enable bit
			g_buffDescTable[1].STAT.BC9    = 0;		// byte count 9 bit
			g_buffDescTable[1].STAT.BC8    = 0;		// byte count 8 bit
			g_buffDescTable[1].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
		} else {
			if(lastDTS == 0) {
				g_buffDescTable[1].STAT.DTS    = 1;		// data toggle synch bit
				g_buffDescTable[1].STAT.KEN    = 0;		// keep enable bit
				g_buffDescTable[1].STAT.INCDIS = 0;		// address increment disable bit
				g_buffDescTable[1].STAT.DTSEN  = 1;		// data toggle synch enable bit
				g_buffDescTable[1].STAT.BSTALL = 0;		// buffer stall enable bit
				g_buffDescTable[1].STAT.BC9    = 0;		// byte count 9 bit
				g_buffDescTable[1].STAT.BC8    = 0;		// byte count 8 bit
				g_buffDescTable[1].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
			} else {
				g_buffDescTable[1].STAT.DTS    = 0;		// data toggle synch bit
				g_buffDescTable[1].STAT.KEN    = 0;		// keep enable bit
				g_buffDescTable[1].STAT.INCDIS = 0;		// address increment disable bit
				g_buffDescTable[1].STAT.DTSEN  = 1;		// data toggle synch enable bit
				g_buffDescTable[1].STAT.BSTALL = 0;		// buffer stall enable bit
				g_buffDescTable[1].STAT.BC9    = 0;		// byte count 9 bit
				g_buffDescTable[1].STAT.BC8    = 0;		// byte count 8 bit
				g_buffDescTable[1].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
			}
		}
	} else {
		USBPrepareForNextSetupTransfer();
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBControlTransferReceive(void) {
	BYTE byteToRead;
	BYTE i;
	
	byteToRead = g_buffDescTable[0].CNT;
	if(byteToRead > g_outPipe.wCount) {
		byteToRead = g_outPipe.wCount;
	} else {
		g_outPipe.wCount = g_outPipe.wCount - byteToRead;
	}
	for(i=0;i<byteToRead;i++) {
		*g_outPipe.bRam++ = g_ctrlTrfData[i];
	}
	if(g_outPipe.wCount > 0) {
		g_buffDescTable[0].CNT = USB_EP0_BUFF_SIZE;
		g_buffDescTable[0].ADR = (BYTE*)&g_ctrlTrfData;
		if(g_buffDescTable[0].STAT.DTS == 0) {
			g_buffDescTable[0].STAT.DTS    = 1;		// data toggle synch bit
			g_buffDescTable[0].STAT.KEN    = 0;		// keep enable bit
			g_buffDescTable[0].STAT.INCDIS = 0;		// address increment disable bit
			g_buffDescTable[0].STAT.DTSEN  = 1;		// data toggle synch enable bit
			g_buffDescTable[0].STAT.BSTALL = 0;		// buffer stall enable bit
			g_buffDescTable[0].STAT.BC9    = 0;		// byte count 9 bit
			g_buffDescTable[0].STAT.BC8    = 0;		// byte count 8 bit
			g_buffDescTable[0].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
		} else {
			g_buffDescTable[0].STAT.DTS    = 0;		// data toggle synch bit
			g_buffDescTable[0].STAT.KEN    = 0;		// keep enable bit
			g_buffDescTable[0].STAT.INCDIS = 0;		// address increment disable bit
			g_buffDescTable[0].STAT.DTSEN  = 1;		// data toggle synch enable bit
			g_buffDescTable[0].STAT.BSTALL = 0;		// buffer stall enable bit
			g_buffDescTable[0].STAT.BC9    = 0;		// byte count 9 bit
			g_buffDescTable[0].STAT.BC8    = 0;		// byte count 8 bit
			g_buffDescTable[0].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
		}
	} else {
		g_buffDescTable[0].ADR = (BYTE*)&g_ctrlTrfSetupPkt;		
		g_outPipe.busy = 0;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBControlTransferTransmit(void) {
	WORD_STRUCT byteToSend;
	BYTE *pDestination;
	
	if(g_inPipe.wCount < USB_EP0_BUFF_SIZE) {
		byteToSend.wordVal = g_inPipe.wCount;
		if(g_shortPacketStatus == SHORT_PKT_NOT_USED) {
			g_shortPacketStatus = SHORT_PKT_PENDING;
		} else if(g_shortPacketStatus == SHORT_PKT_PENDING) {
			g_shortPacketStatus = SHORT_PKT_SENT;
		}
	} else {
		byteToSend.wordVal = USB_EP0_BUFF_SIZE;
	}
	g_buffDescTable[1].STAT.BC9 = 0;							// copy value in byteToSend into COUNT
	g_buffDescTable[1].STAT.BC8 = 0;
	g_buffDescTable[1].STAT.STATVal |= byteToSend.byte.HB;
	g_buffDescTable[1].CNT = byteToSend.byte.LB;
	
	g_inPipe.wCount = g_inPipe.wCount - byteToSend.wordVal;
	
	pDestination = (BYTE*)g_ctrlTrfData;
	
	while(byteToSend.wordVal) {						// for each byte we have to send . . .
		*pDestination = *g_inPipe.bRom;				// assign contents of in pipe into g_ctrlTrfData
		*pDestination++;							// increment one pointer
		*g_inPipe.bRom++;							// increment other pointer
		byteToSend.wordVal--;						// decrement counter
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void USBPrepareForNextSetupTransfer(void) {
	if((g_controlTransferState == CTRL_TRF_RX) &&
	   (UCONbits.PKTDIS == 1) &&
	   (g_buffDescTable[0].CNT == sizeof(CTRL_TRF_SETUP)) &&
	   (g_buffDescTable[0].STAT.PID == SETUP_TOKEN) &&
	   (g_buffDescTable[0].STAT.UOWN == 0)) {
		BYTE setup_cnt;
		g_buffDescTable[0].ADR = (BYTE*)&g_ctrlTrfSetupPkt;
		for(setup_cnt = 0; setup_cnt < sizeof(CTRL_TRF_SETUP); setup_cnt++) {
			*(((BYTE*)&g_ctrlTrfSetupPkt) + setup_cnt) = *(((BYTE*)&g_ctrlTrfData) + setup_cnt);
		}
	} else {
		g_controlTransferState = CTRL_TRF_WAIT_SETUP;
		g_buffDescTable[0].CNT = USB_EP0_BUFF_SIZE;
		g_buffDescTable[0].ADR = (BYTE*)&g_ctrlTrfSetupPkt;
		g_buffDescTable[0].STAT.DTS    = 0;		// data toggle synch bit
		g_buffDescTable[0].STAT.KEN    = 0;		// keep enable bit
		g_buffDescTable[0].STAT.INCDIS = 0;		// address increment disable bit
		g_buffDescTable[0].STAT.DTSEN  = 1;		// data toggle synch enable bit
		g_buffDescTable[0].STAT.BSTALL = 1;		// buffer stall enable bit
		g_buffDescTable[0].STAT.BC9    = 0;		// byte count 9 bit
		g_buffDescTable[0].STAT.BC8    = 0;		// byte count 8 bit
		g_buffDescTable[0].STAT.UOWN   = 1;		// UOWN bit (SIE owns buffer)
		
		g_buffDescTable[1].STAT.UOWN   = 0;		// UOWN bit (CPU owns buffer)
		g_buffDescTable[1].STAT.DTS    = 0;		// data toggle synch bit
		g_buffDescTable[1].STAT.KEN    = 0;		// keep enable bit
		g_buffDescTable[1].STAT.INCDIS = 0;		// address increment disable bit
		g_buffDescTable[1].STAT.DTSEN  = 0;		// data toggle synch enable bit
		g_buffDescTable[1].STAT.BSTALL = 0;		// buffer stall enable bit
		g_buffDescTable[1].STAT.BC9    = 0;		// byte count 9 bit
		g_buffDescTable[1].STAT.BC8    = 0;		// byte count 8 bit
		
	}
	if(g_outPipe.busy == 1) {
		g_outPipe.busy = 0;
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void receiveViaUSB(void) {
	while(UIRbits.TRNIF != 1) { }						// wait here until any pending transactions are complete
		
	if(g_buffDescTable[2].STAT.UOWN == 0) {					// verify USB circuitry does not own from host USB buffer
		transferFromHostToDeviceViaEP1((BYTE*)&g_fromHostToDeviceBuffer[1], 64);		// get packet from host
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void sendViaUSB(void) {
	while(UIRbits.TRNIF != 1) { }						// wait here until any pending transactions are complete
	
	if(g_buffDescTable[3].STAT.UOWN == 0) {					// verify USB circuitry does not own to host USB buffer
		transferFromDeviceToHostViaEP1((BYTE*)&g_fromDeviceToHostBuffer[1], 64);		// send packet to host
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void transferFromHostToDeviceViaEP1(BYTE* pPacketData, BYTE packetLength) {	
	g_buffDescTable[2].STAT.DTS    = !g_buffDescTable[2].STAT.DTS;	// toggle the data toggle synch bit
	g_buffDescTable[2].STAT.KEN    = 0;								// clear keep enable bit
	g_buffDescTable[2].STAT.INCDIS = 0;								// clear address increment disable bit
	g_buffDescTable[2].STAT.DTSEN  = 1;								// set data toggle synch enable bit
	g_buffDescTable[2].STAT.BSTALL = 0;								// clear buffer stall enable bit
	g_buffDescTable[2].STAT.BC9    = 0;								// clear byte count 9 bit
	g_buffDescTable[2].STAT.BC8    = 0;								// clear byte count 8 bit
	g_buffDescTable[2].STAT.UOWN   = 1;								// set UOWN bit (SIE owns buffer)
	
	g_buffDescTable[2].CNT = packetLength;
	g_buffDescTable[2].ADR = pPacketData;
}

///////////////////////////////////////////////////////////////////////////////////////////////////
void transferFromDeviceToHostViaEP1(BYTE* pPacketData, BYTE packetLength) {			
	g_buffDescTable[3].STAT.DTS    = !g_buffDescTable[3].STAT.DTS;	// toggle the data toggle synch bit
	g_buffDescTable[3].STAT.KEN    = 0;								// clear keep enable bit
	g_buffDescTable[3].STAT.INCDIS = 0;								// clear address increment disable bit
	g_buffDescTable[3].STAT.DTSEN  = 1;								// set data toggle synch enable bit
	g_buffDescTable[3].STAT.BSTALL = 0;								// clear buffer stall enable bit
	g_buffDescTable[3].STAT.BC9    = 0;								// clear byte count 9 bit
	g_buffDescTable[3].STAT.BC8    = 0;								// clear byte count 8 bit
	g_buffDescTable[3].STAT.UOWN   = 1;								// set UOWN bit (SIE owns buffer)
	
	g_buffDescTable[3].CNT = packetLength;
	g_buffDescTable[3].ADR = pPacketData;
}

Code:
// USBFunctions.h

// #defines, typedefs, structs ////////////////////////////////////////////////////////////////////

		// USB device states - use with g_USBDeviceState (BYTE data type)
#define DETACHED_STATE		0x00
#define ATTACHED_STATE		0x01
#define POWERED_STATE		0x02
#define DEFAULT_STATE		0x04
#define ADR_PENDING_STATE	0x08
#define ADDRESS_STATE		0x10
#define CONFIGURED_STATE	0x20

		// for use with g_controlTransferState
#define CTRL_TRF_WAIT_SETUP		0
#define CTRL_TRF_TX				1
#define CTRL_TRF_RX				2

		// for use with g_shortPacketStatus
#define SHORT_PKT_NOT_USED	0
#define SHORT_PKT_PENDING	1
#define SHORT_PKT_SENT		2

		// for use with g_ctrlTrfSetupPkt.bDescriptorType
#define USB_DESCRIPTOR_DEVICE			0x01
#define USB_DESCRIPTOR_CONFIGURATION	0x02
#define USB_DESCRIPTOR_STRING			0x03
#define USB_DESCRIPTOR_INTERFACE		0x04
#define USB_DESCRIPTOR_ENDPOINT			0x05
#define USB_DESCRIPTOR_DEVICE_QUALIFIER 0x06
#define USB_DESCRIPTOR_OTHER_SPEED		0x07
#define USB_DESCRIPTOR_INTERFACE_POWER	0x08
#define USB_DESCRIPTOR_OTG				0x09

typedef enum _BOOL { FALSE = 0, TRUE = 1 } BOOL;

#define NULL 0

typedef unsigned char		BYTE;		// unsigned 8-bit int
typedef unsigned short		WORD;		// unsigned 16-bit int
typedef unsigned long		UINT32;		// unsigned 32-bit int
typedef unsigned long long	UINT64;		// unsigned 64-bit int

typedef union _WORD_VAL {		// 2 bytes
	WORD wordVal;
	struct {
		BYTE LB;
		BYTE HB;
	} byte;
} WORD_STRUCT;

typedef struct _USB_DEVICE_DESCRIPTOR {
	BYTE bLength;
	BYTE bDescriptorType;
	WORD bcdUSB;
	BYTE bDeviceClass;
	BYTE bDeviceSubClass;
	BYTE bDeviceProtocol;
	BYTE bMaxPacketSize0;
	WORD idVendor;
	WORD idProduct;
	WORD bcdDevice;
	BYTE iManufacturer;
	BYTE iProduct;
	BYTE iSerialNumber;
	BYTE bNumConfigurations;
} USB_DEVICE_DESCRIPTOR;

typedef struct _USB_HID_DSC {
	BYTE bLength;
	BYTE bDescriptorType;
	WORD bcdHID;
	BYTE bCountryCode;
	BYTE bNumDsc;
} USB_HID_DSC;

typedef struct {				// 4 bytes
	union {							// BDnSTAT imitation
		struct {					// CPU mode (UOWN == 0)
			unsigned BC8:1;
			unsigned BC9:1;
			unsigned BSTALL:1;
			unsigned DTSEN:1;
			unsigned INCDIS:1;
			unsigned KEN:1;
			unsigned DTS:1;
			unsigned UOWN:1;
		};
		struct {
			unsigned BC8:1;			// SIE mode (UOWN == 1)
			unsigned BC9:1;
			unsigned PID:4;
			unsigned :1;
			unsigned UOWN:1;
		};
		BYTE STATVal;
	} STAT;
	BYTE CNT;						// size of buffer in bytes
	BYTE *ADR;
} BDT_ENTRY;

#define USB_EP0_BUFF_SIZE		8

#define HID_INTERFACE_ID		0x00

#define HID_NUM_OF_DSC			1		// for use with config descriptor only

#define HID_RPT01_SIZE			29		// HID report 1 size in bytes, used with descriptors only

#define _EP_IN		0x80		// for use with descriptor declarations only
#define _EP_OUT		0x00		//

		// configuration attributes
#define _DEFAULT	(0x01 << 7)		// default value, bit 7 is set			used with descriptor declarations only
#define _SELF		(0x01 << 6)		// self-powered (supports if set)		why are these done as bit shifts ??!!
#define _RWU		(0x01 << 5)		// remote wakeup (supports if set)

		// endpoint transfer type, for use with config descriptor only
#define _CTRL	0x00		// control transfer
#define _ISO	0x01		// isochronous transfer
#define _BULK	0x02		// bulk transfer
#define _INT	0x03		// interrupt transfer

		// HID interface class code, for use with config descriptor only
#define HID_INTF		0x03

// every setup packet has 8 bytes, this structure allows direct access to the various members of the control transfer
typedef union _CTRL_TRF_SETUP {				// 8 bytes
	struct {
		BYTE bmRequestType;		// bit map request type
		BYTE bRequest;
		WORD wValue;
		WORD wIndex;
		WORD wLength;
	};
	struct {
		unsigned Recipient:5;
		unsigned RequestType:2;
		unsigned DataDir:1;
		unsigned :8;
		unsigned :8;
		unsigned :8;
		unsigned :8;
		unsigned :8;
		unsigned :8;
		unsigned :8;
	};
	struct {
		unsigned :8;
		unsigned :8;
		BYTE bDscIndex;
		BYTE bDescriptorType;
		WORD wLangID;
		unsigned :8;
		unsigned :8;
	};
	struct {
		unsigned :8;
		unsigned :8;
		BYTE bDevADR;
		BYTE bDevADRH;
		unsigned :8;
		unsigned :8;
		unsigned :8;
		unsigned :8;
	};
	struct {
		unsigned :8;
		unsigned :8;
		BYTE bConfigurationValue;
		BYTE bCfgRSD;
		unsigned :8;
		unsigned :8;
		unsigned :8;
		unsigned :8;
	};
} CTRL_TRF_SETUP;

		// pipe structure, used to keep track of data that is sent out of the stack automatically
typedef struct {		// 7 bytes
	rom BYTE *bRom;
	rom WORD *wRom;
	BYTE busy;
	WORD wCount;
} IN_PIPE;

typedef struct {		// 5 bytes
	BYTE *bRam;	
	BYTE busy;
	WORD wCount;
} OUT_PIPE;

			// for use with g_crtlTrfSetupPkt.bRequest, see USB 2.0 spec. table 9-3
#define GET_STATUS			0
#define CLEAR_FEATURE		1
#define SET_FEATURE			3
#define SET_ADDRESS			5
#define GET_DESCRIPTOR		6
#define SET_DESCRIPTOR		7
#define GET_CONFIGURATION	8
#define SET_CONFIGURATION	9
#define GET_INTERFACE		10
#define SET_INTERFACE		11
#define SYNCH_FRAME			12

			// for use with g_buffDescTable.STAT.PID, note that number is 4-bit
#define SETUP_TOKEN		0b1101	// 13 base 10
#define OUT_TOKEN		0b0001	//  1 base 10
#define IN_TOKEN		0b1001	//  9 base 10

			// for use with g_ctrlTrfSetupPkt.DataDir
#define HOST_TO_DEV		0
#define DEV_TO_HOST		1

			// for use with CTRL_TRF_SETUP.RequestType
#define STANDARD	0x00
#define CLASS		0x01
#define VENDOR		0x02

			// for use with g_ctrlTrfSetupPkt.Recipient
#define RECIPIENT_DEVICE		0
#define RECIPIENT_INTERFACE		1
#define RECIPIENT_ENDPOINT		2
#define RECIPIENT_OTHER			3

			// for use with g_ctrlTrfSetupPkt.bRequest
#define GET_REPORT		0x01
#define GET_IDLE		0x02
#define GET_PROTOCOL	0x03
#define SET_REPORT		0x09
#define SET_IDLE		0x0A
#define SET_PROTOCOL	0x0B

			// for use with g_ctrlTrfSetupPkt.bDescriptorType, and also in descriptor declarations
#define HID_DESCRIPTOR		0x21
#define REPORT_DESCRIPTOR	0x22
#define PHY_DESCRIPTOR		0x23

#define CLK_FREQ 48000000

// function prototypes ////////////////////////////////////////////////////////////////////////////
void USBInit(void);
void USBTasks(void);
void USBWakeFromSuspend(void);
void USBSuspend(void);
void USBStallHandler(void);
void USBEP0ControlTransfer(void);
void USBSetupControlTransfer(void);
void USBOutControlTransfer(void);
void USBInControlTransfer(void);
void USBCheckStandardRequest(void);
void USBCheckHIDRequest(void);
void USBFinishControlTransferStuff(void);
void USBControlTransferReceive(void);
void USBControlTransferTransmit(void);
void USBPrepareForNextSetupTransfer(void);
void receiveViaUSB(void);
void sendViaUSB(void);
void transferFromHostToDeviceViaEP1(BYTE* pPacketData, BYTE packetLength);
void transferFromDeviceToHostViaEP1(BYTE* pPacketData, BYTE packetLength);

Code:
// File: Application_18f4550.lkr

// Use this linker for the USB application that will be self programmed by the HID bootloader.
// The HID bootloader project itself uses the BootModified.18f4450.lkr file instead.

// THIS LINKER SCRIPT HAS BEEN MODIFIED...  This version is intended to be used
// with the "PROGRAMMABLE_WITH_USB_HID_BOOTLOADER" bootloader.  The HID
// bootloader occupies memory ranges 0x000-0xFFF.  In order for the code generated
// by this project to work with the bootloader, the linker must not put any code
// in the 0x00-0xFFF address range.

// This linker script was originated from the 18f4550.lkr file provided by
// the MCC18 distribution.

LIBPATH .

FILES c018i.o
FILES clib.lib
FILES p18f4550.lib

CODEPAGE   NAME=bootloader START=0x0          	   END=0xFFF          PROTECTED
CODEPAGE   NAME=vectors    START=0x1000       	   END=0x1029	  	  PROTECTED
CODEPAGE   NAME=page       START=0x102A            END=0x7FFF
CODEPAGE   NAME=idlocs     START=0x200000          END=0x200007       PROTECTED
CODEPAGE   NAME=config     START=0x300000          END=0x30000D       PROTECTED
CODEPAGE   NAME=devid      START=0x3FFFFE          END=0x3FFFFF       PROTECTED
CODEPAGE   NAME=eedata     START=0xF00000          END=0xF000FF       PROTECTED

ACCESSBANK NAME=accessram  START=0x0            END=0x5F
DATABANK   NAME=gpr0       START=0x60           END=0xFF
DATABANK   NAME=gpr1       START=0x100          END=0x1FF
DATABANK   NAME=gpr2       START=0x200          END=0x2FF
DATABANK   NAME=gpr3       START=0x300          END=0x3FF
DATABANK   NAME=usb4       START=0x400          END=0x4FF          PROTECTED
DATABANK   NAME=usb5       START=0x500          END=0x5FF          PROTECTED
DATABANK   NAME=usb6       START=0x600          END=0x6FF          PROTECTED
DATABANK   NAME=usb7       START=0x700          END=0x7FF          PROTECTED
ACCESSBANK NAME=accesssfr  START=0xF60          END=0xFFF          PROTECTED

SECTION    NAME=CONFIG     ROM=config

STACK SIZE=0x100 RAM=gpr3

SECTION	   NAME=USB_VARS   RAM=usb4


Software code

Code:
/*
Notes on using this class:

1) To get a successfull compile, it is necessary to go to:

Project -> [project name] Properties -> Build -> check 'Allow Unsafe Code'

2) change the namespace name to match your project

*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Windows.Forms;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Threading;

///////////////////////////////////////////////////////////////////////////////////////////////////
namespace USBMotorController {										// NOTE: change this to match your namespace name
	
	/////////////////////////////////////////////////////////////////////////////////////////////////
	public class USBClass : Form {
		//
		// constants //////////////////////////////////////////////////////////////////////////////////
		public const byte CONNECTION_NOT_SUCCESSFUL = 0;		// for use with g_connectionState
		public const byte CONNECTION_SUCCESSFUL = 1;				//

		// constant definitions from setupapi.h, which we aren't allowed to include directly since this is C#
		internal const uint DIGCF_PRESENT = 0x02;
		internal const uint DIGCF_DEVICEINTERFACE = 0x10;
		// constants for CreateFile() and other file I/O functions
		internal const short FILE_ATTRIBUTE_NORMAL = 0x80;
		internal const short INVALID_HANDLE_VALUE = -1;
		internal const uint GENERIC_READ = 0x80000000;
		internal const uint GENERIC_WRITE = 0x40000000;
		internal const uint CREATE_NEW = 1;
		internal const uint CREATE_ALWAYS = 2;
		internal const uint OPEN_EXISTING = 3;
		internal const uint FILE_SHARE_READ = 0x00000001;
		internal const uint FILE_SHARE_WRITE = 0x00000002;
		// constant definitions for certain WM_DEVICECHANGE messages
		internal const uint WM_DEVICECHANGE = 0x0219;
		internal const uint DBT_DEVICEARRIVAL = 0x8000;
		internal const uint DBT_DEVICEREMOVEPENDING = 0x8003;
		internal const uint DBT_DEVICEREMOVECOMPLETE = 0x8004;
		internal const uint DBT_CONFIGCHANGED = 0x0018;
		// other constant definitions
		internal const uint DBT_DEVTYP_DEVICEINTERFACE = 0x05;
		internal const uint DEVICE_NOTIFY_WINDOW_HANDLE = 0x00;

		internal const uint DEVICE_NOTIFY_SERVICE_HANDLE = 0x01;
		internal const uint DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 0x04;

		internal const uint ERROR_SUCCESS = 0x00;
		internal const uint ERROR_NO_MORE_ITEMS = 0x00000103;
		internal const uint SPDRP_HARDWAREID = 0x00000001;

		// structs ////////////////////////////////////////////////////////////////////////////////////
		// struct definitions from setupapi.h, which we aren't allowed to include directly since this is C#
		internal struct SP_DEVICE_INTERFACE_DATA
		{
			internal uint cbSize;               //DWORD
			internal Guid InterfaceClassGuid;   //GUID
			internal uint Flags;                //DWORD
			internal uint Reserved;             //ULONG_PTR MSDN says ULONG_PTR is "typedef unsigned __int3264 ULONG_PTR;"  
		}

		internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
		{
			internal uint cbSize;               //DWORD
			internal char[] DevicePath;         //TCHAR array of any size
		}

		internal struct SP_DEVINFO_DATA
		{
			internal uint cbSize;       //DWORD
			internal Guid ClassGuid;    //GUID
			internal uint DevInst;      //DWORD
			internal uint Reserved;     //ULONG_PTR  MSDN says ULONG_PTR is "typedef unsigned __int3264 ULONG_PTR;"  
		}

		internal struct DEV_BROADCAST_DEVICEINTERFACE
		{
			internal uint dbcc_size;            //DWORD
			internal uint dbcc_devicetype;      //DWORD
			internal uint dbcc_reserved;        //DWORD
			internal Guid dbcc_classguid;       //GUID
			internal char[] dbcc_name;          //TCHAR array
		}

		// Windows API function DLL imports ///////////////////////////////////////////////////////////
		//Returns a HDEVINFO type for a device information set.  We will need the 
		//HDEVINFO as in input parameter for calling many of the other SetupDixxx() functions.
		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		internal static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid,     //LPGUID    Input: Need to supply the class GUID. 
																											IntPtr Enumerator,      //PCTSTR    Input: Use NULL here, not important for our purposes
																											IntPtr hwndParent,      //HWND      Input: Use NULL here, not important for our purposes
																											uint Flags);            //DWORD     Input: Flags describing what kind of filtering to use.

		//Gives us "PSP_DEVICE_INTERFACE_DATA" which contains the Interface specific GUID (different
		//from class GUID).  We need the interface GUID to get the device path.
		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		internal static extern bool SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet,           //Input: Give it the HDEVINFO we got from SetupDiGetClassDevs()
																														IntPtr DeviceInfoData,          //Input (optional)
																														ref Guid InterfaceClassGuid,    //Input 
																														uint MemberIndex,               //Input: "Index" of the device you are interested in getting the path for.
																														ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);    //Output: This function fills in an "SP_DEVICE_INTERFACE_DATA" structure.

		//SetupDiDestroyDeviceInfoList() frees up memory by destroying a DeviceInfoList
		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		internal static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);					//Input: Give it a handle to a device info list to deallocate from RAM.

		//SetupDiEnumDeviceInfo() fills in an "SP_DEVINFO_DATA" structure, which we need for SetupDiGetDeviceRegistryProperty()
		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		internal static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet,
																											uint MemberIndex,
																											ref SP_DEVINFO_DATA DeviceInterfaceData);

		//SetupDiGetDeviceRegistryProperty() gives us the hardware ID, which we use to check to see if it has matching VID/PID
		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		internal static extern bool SetupDiGetDeviceRegistryProperty(IntPtr DeviceInfoSet,
																																 ref SP_DEVINFO_DATA DeviceInfoData,
																																 uint Property,
																																 ref uint PropertyRegDataType,
																																 IntPtr PropertyBuffer,
																																 uint PropertyBufferSize,
																																 ref uint RequiredSize);

		//SetupDiGetDeviceInterfaceDetail() gives us a device path, which is needed before CreateFile() can be used.
		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		internal static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet,                   //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs()
																																ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData,                    //Input: Pointer to an structure which defines the device interface.  
																																IntPtr DeviceInterfaceDetailData,      //Output: Pointer to a SP_DEVICE_INTERFACE_DETAIL_DATA structure, which will receive the device path.
																																uint DeviceInterfaceDetailDataSize,     //Input: Number of bytes to retrieve.
																																ref uint RequiredSize,                  //Output (optional): The number of bytes needed to hold the entire struct 
																																IntPtr DeviceInfoData);                 //Output (optional): Pointer to a SP_DEVINFO_DATA structure

		//Overload for SetupDiGetDeviceInterfaceDetail().  Need this one since we can't pass NULL pointers directly in C#.
		[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		internal static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet,                   //Input: Wants HDEVINFO which can be obtained from SetupDiGetClassDevs()
																																ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData,               //Input: Pointer to an structure which defines the device interface.  
																																IntPtr DeviceInterfaceDetailData,       //Output: Pointer to a SP_DEVICE_INTERFACE_DETAIL_DATA structure, which will contain the device path.
																																uint DeviceInterfaceDetailDataSize,     //Input: Number of bytes to retrieve.
																																IntPtr RequiredSize,                    //Output (optional): Pointer to a DWORD to tell you the number of bytes needed to hold the entire struct 
																																IntPtr DeviceInfoData);                 //Output (optional): Pointer to a SP_DEVINFO_DATA structure

		//Need this function for receiving all of the WM_DEVICECHANGE messages.  See MSDN documentation for
		//description of what this function does/how to use it. Note: name is remapped "RegisterDeviceNotificationUM" to
		//avoid possible build error conflicts.
		[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		internal static extern IntPtr RegisterDeviceNotification(IntPtr hRecipient,
																														 IntPtr NotificationFilter,
																														 uint Flags);

		//Takes in a device path and opens a handle to the device.
		[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		static extern SafeFileHandle CreateFile(string lpFileName,
																						uint dwDesiredAccess,
																						uint dwShareMode,
																						IntPtr lpSecurityAttributes,
																						uint dwCreationDisposition,
																						uint dwFlagsAndAttributes,
																						IntPtr hTemplateFile);

		//Uses a handle (created with CreateFile()), and lets us write USB data to the device.
		[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		static extern bool WriteFile(SafeFileHandle hFile,
																 byte[] lpBuffer,
																 uint nNumberOfBytesToWrite,
																 ref uint lpNumberOfBytesWritten,
																 IntPtr lpOverlapped);

		//Uses a handle (created with CreateFile()), and lets us read USB data from the device.
		[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
		static extern bool ReadFile(SafeFileHandle hFile,
																IntPtr lpBuffer,
																uint nNumberOfBytesToRead,
																ref uint lpNumberOfBytesRead,
																IntPtr lpOverlapped);

		// member variables ///////////////////////////////////////////////////////////////////////////
																										// modify to match the Vendor ID (VID) and Product ID (PID) in the firmware USB Device Descriptor
																										// default is set to match the Microchip examples
		public String deviceID = "Vid_04D8&Pid_003F";		// use the format "Vid_xxxx&Pid_xxxx" where xxxx is a 16-bit hex number
		
		public int connectionState;							// for USB connection state

		public byte[] fromDeviceToHostBuffer = new byte[65];		// for data transfer to host, byte 0 => "Report ID", init to zero and never use !!

		public byte[] fromHostToDeviceBuffer = new byte[65];		// for data transfer to device, byte 0 => "Report ID", init to zero and never use !!

		bool AttachedState = false;						//Need to keep track of the USB device attachment status for proper plug and play operation.
		SafeFileHandle WriteHandleToUSBDevice = null;
		SafeFileHandle ReadHandleToUSBDevice = null;
		String DevicePath = null;   //Need the find the proper device path before you can open file handles.

		//Globally Unique Identifier (GUID) for HID class devices.  Windows uses GUIDs to identify things.
		Guid InterfaceClassGuid = new Guid(0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);

		// constructor ////////////////////////////////////////////////////////////////////////////////
		public USBClass()
		{
			fromHostToDeviceBuffer[0] = 0;				// The first byte in the I/O buffers is the "Report ID" and does not get transmitted over the
			fromDeviceToHostBuffer[0] = 0;				// USB bus, never change these !!!  Start using the buffers for actual data at index 1.
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////
		public int attemptUSBConnection()
		{

			// Register for WM_DEVICECHANGE notifications.  This code uses these messages to detect plug and play connection/disconnection events for USB devices
			DEV_BROADCAST_DEVICEINTERFACE DeviceBroadcastHeader = new DEV_BROADCAST_DEVICEINTERFACE();
			DeviceBroadcastHeader.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
			DeviceBroadcastHeader.dbcc_size = (uint)Marshal.SizeOf(DeviceBroadcastHeader);
			DeviceBroadcastHeader.dbcc_reserved = 0;	//Reserved says not to use...
			DeviceBroadcastHeader.dbcc_classguid = InterfaceClassGuid;

			// Need to get the address of the DeviceBroadcastHeader to call RegisterDeviceNotification(), but
			// can't use "&DeviceBroadcastHeader".  Instead, using a roundabout means to get the address by 
			// making a duplicate copy using Marshal.StructureToPtr().
			IntPtr pDeviceBroadcastHeader = IntPtr.Zero;				// Make a pointer.
			pDeviceBroadcastHeader = Marshal.AllocHGlobal(Marshal.SizeOf(DeviceBroadcastHeader));		// allocate memory for a new DEV_BROADCAST_DEVICEINTERFACE structure, and return the address 
			Marshal.StructureToPtr(DeviceBroadcastHeader, pDeviceBroadcastHeader, false);						// Copies the DeviceBroadcastHeader structure into the memory already allocated at DeviceBroadcastHeaderWithPointer
			RegisterDeviceNotification(this.Handle, pDeviceBroadcastHeader, DEVICE_NOTIFY_WINDOW_HANDLE);

			// Now make an initial attempt to find the USB device, if it was already connected to the PC and enumerated prior to launching the application.
			// If it is connected and present, we should open read and write handles to the device so we can communicate with it later.
			// If it was not connected, we will have to wait until the user plugs the device in, and the WM_DEVICECHANGE callback function can process
			// the message and again search for the device.

			IntPtr DeviceInfoTable = IntPtr.Zero;
			SP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA();
			SP_DEVICE_INTERFACE_DETAIL_DATA DetailedInterfaceDataStructure = new SP_DEVICE_INTERFACE_DETAIL_DATA();
			SP_DEVINFO_DATA DevInfoData = new SP_DEVINFO_DATA();

			uint InterfaceIndex = 0;
			uint dwRegType = 0;
			uint dwRegSize = 0;
			uint dwRegSize2 = 0;
			uint StructureSize = 0;
			IntPtr PropertyValueBuffer = IntPtr.Zero;
			bool MatchFound = false;
			uint ErrorStatus;
			uint loopCounter = 0;

			String DeviceIDToFind = deviceID;

			// First populate a list of plugged in devices (by specifying "DIGCF_PRESENT"), which are of the specified class GUID. 
			DeviceInfoTable = SetupDiGetClassDevs(ref InterfaceClassGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

			if (DeviceInfoTable == IntPtr.Zero) return (CONNECTION_NOT_SUCCESSFUL);

			// Now look through the list we just populated.  We are trying to see if any of them match our device. 
			while (true)
			{
				InterfaceDataStructure.cbSize = (uint)Marshal.SizeOf(InterfaceDataStructure);
				if (SetupDiEnumDeviceInterfaces(DeviceInfoTable, IntPtr.Zero, ref InterfaceClassGuid, InterfaceIndex, ref InterfaceDataStructure))
				{
					ErrorStatus = (uint)Marshal.GetLastWin32Error();
					if (ErrorStatus == ERROR_NO_MORE_ITEMS)
					{							// did we reach the end of the list of matching devices in the DeviceInfoTable?
						// cound not find the device.  Must not have been attached.
						SetupDiDestroyDeviceInfoList(DeviceInfoTable);			// clean up the old structure we no longer need.
						return (CONNECTION_NOT_SUCCESSFUL);
					}
				}
				else
				{																			// else some other kind of unknown error ocurred...
					ErrorStatus = (uint)Marshal.GetLastWin32Error();
					SetupDiDestroyDeviceInfoList(DeviceInfoTable);	// Clean up the old structure we no longer need.
					return (CONNECTION_NOT_SUCCESSFUL);
				}

				// Now retrieve the hardware ID from the registry.  The hardware ID contains the VID and PID, which we will then 
				// check to see if it is the correct device or not.

				// Initialize an appropriate SP_DEVINFO_DATA structure.  We need this structure for SetupDiGetDeviceRegistryProperty().
				DevInfoData.cbSize = (uint)Marshal.SizeOf(DevInfoData);
				SetupDiEnumDeviceInfo(DeviceInfoTable, InterfaceIndex, ref DevInfoData);

				// First query for the size of the hardware ID, so we can know how big a buffer to allocate for the data.
				SetupDiGetDeviceRegistryProperty(DeviceInfoTable, ref DevInfoData, SPDRP_HARDWAREID, ref dwRegType, IntPtr.Zero, 0, ref dwRegSize);

				// Allocate a buffer for the hardware ID.
				// Should normally work, but could throw exception "OutOfMemoryException" if not enough resources available.
				PropertyValueBuffer = Marshal.AllocHGlobal((int)dwRegSize);

				// Retrieve the hardware IDs for the current device we are looking at.  PropertyValueBuffer gets filled with a 
				// REG_MULTI_SZ (array of null terminated strings).  To find a device, we only care about the very first string in the
				// buffer, which will be the "device ID".  The device ID is a string which contains the VID and PID, in the example 
				// format "Vid_04d8&Pid_003f".
				SetupDiGetDeviceRegistryProperty(DeviceInfoTable, ref DevInfoData, SPDRP_HARDWAREID, ref dwRegType, PropertyValueBuffer, dwRegSize, ref dwRegSize2);

				// Now check if the first string in the hardware ID matches the device ID of the USB device we are trying to find.
				String DeviceIDFromRegistry = Marshal.PtrToStringUni(PropertyValueBuffer);	// Make a new string, fill it with the contents from the PropertyValueBuffer

				Marshal.FreeHGlobal(PropertyValueBuffer);		// no longer need the PropertyValueBuffer, free the memory to prevent potential memory leaks

				// Convert both strings to lower case.  This makes the code more robust/portable accross OS Versions
				DeviceIDFromRegistry = DeviceIDFromRegistry.ToLowerInvariant();
				DeviceIDToFind = DeviceIDToFind.ToLowerInvariant();
				// Now check if the hardware ID we are looking at contains the correct VID/PID
				MatchFound = DeviceIDFromRegistry.Contains(DeviceIDToFind);
				if (MatchFound == true)
				{
					// Device must have been found.  In order to open I/O file handle(s), we will need the actual device path first.
					// We can get the path by calling SetupDiGetDeviceInterfaceDetail(), however, we have to call this function twice:  The first
					// time to get the size of the required structure/buffer to hold the detailed interface data, then a second time to actually 
					// get the structure (after we have allocated enough memory for the structure.)
					DetailedInterfaceDataStructure.cbSize = (uint)Marshal.SizeOf(DetailedInterfaceDataStructure);

					// First call populates "StructureSize" with the correct value
					SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, ref InterfaceDataStructure, IntPtr.Zero, 0, ref StructureSize, IntPtr.Zero);

					// Need to call SetupDiGetDeviceInterfaceDetail() again, this time specifying a pointer to a SP_DEVICE_INTERFACE_DETAIL_DATA buffer with the correct size of RAM allocated.
					// First need to allocate the unmanaged buffer and get a pointer to it.
					IntPtr pUnmanagedDetailedInterfaceDataStructure = IntPtr.Zero;		// Declare a pointer.
					pUnmanagedDetailedInterfaceDataStructure = Marshal.AllocHGlobal((int)StructureSize);    // Reserve some unmanaged memory for the structure.
					DetailedInterfaceDataStructure.cbSize = 6;	// Initialize the cbSize parameter (4 bytes for DWORD + 2 bytes for unicode null terminator)
					Marshal.StructureToPtr(DetailedInterfaceDataStructure, pUnmanagedDetailedInterfaceDataStructure, false); //Copy managed structure contents into the unmanaged memory buffer.

					// Now call SetupDiGetDeviceInterfaceDetail() a second time to receive the device path in the structure at pUnmanagedDetailedInterfaceDataStructure.
					if (SetupDiGetDeviceInterfaceDetail(DeviceInfoTable, ref InterfaceDataStructure, pUnmanagedDetailedInterfaceDataStructure, StructureSize, IntPtr.Zero, IntPtr.Zero))
					{
						// Need to extract the path information from the unmanaged "structure".  The path starts at (pUnmanagedDetailedInterfaceDataStructure + sizeof(DWORD)).
						IntPtr pToDevicePath = new IntPtr((uint)pUnmanagedDetailedInterfaceDataStructure.ToInt32() + 4);  //Add 4 to the pointer (to get the pointer to point to the path, instead of the DWORD cbSize parameter)
						DevicePath = Marshal.PtrToStringUni(pToDevicePath); // Now copy the path information into the globally defined DevicePath String.

						// We now have the proper device path, and we can finally use the path to open I/O handle(s) to the device.
						SetupDiDestroyDeviceInfoList(DeviceInfoTable);									// Clean up the old structure we no longer need.
						Marshal.FreeHGlobal(pUnmanagedDetailedInterfaceDataStructure);  // No longer need this unmanaged SP_DEVICE_INTERFACE_DETAIL_DATA buffer.  We already extracted the path information.

						uint ErrorStatusWrite;
						uint ErrorStatusRead;
						// we now have the proper device path, and we can finally open read and write handles to the device.
						WriteHandleToUSBDevice = CreateFile(DevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
						ErrorStatusWrite = (uint)Marshal.GetLastWin32Error();
						ReadHandleToUSBDevice = CreateFile(DevicePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
						ErrorStatusRead = (uint)Marshal.GetLastWin32Error();

						if ((ErrorStatusWrite == ERROR_SUCCESS) && (ErrorStatusRead == ERROR_SUCCESS))
						{
							return (CONNECTION_SUCCESSFUL);
						}
						else
						{									// for some reason the device was physically plugged in, but one or both of the read/write handles didn't open successfully...
							if (ErrorStatusWrite == ERROR_SUCCESS) WriteHandleToUSBDevice.Close();
							if (ErrorStatusRead == ERROR_SUCCESS) ReadHandleToUSBDevice.Close();
							return (CONNECTION_NOT_SUCCESSFUL);
						}
					}
					else
					{																	// Some unknown failure occurred
						uint ErrorCode = (uint)Marshal.GetLastWin32Error();
						SetupDiDestroyDeviceInfoList(DeviceInfoTable);	// Clean up the old structure.
						Marshal.FreeHGlobal(pUnmanagedDetailedInterfaceDataStructure);  // No longer need this unmanaged SP_DEVICE_INTERFACE_DETAIL_DATA buffer.  We already extracted the path information.
						return (CONNECTION_NOT_SUCCESSFUL);
					}
				}
				InterfaceIndex++;
				loopCounter++;
				if (loopCounter > 200)
				{														// if more than 200 times through the loop . . .
					return (CONNECTION_NOT_SUCCESSFUL);								// give up and bail
				}
			}	// end of while(true)			
		}

		///////////////////////////////////////////////////////////////////////////////////////////////
		public void receiveViaUSB()
		{
			uint BytesRead = 0;
			ReadFileManagedBuffer(ReadHandleToUSBDevice, fromDeviceToHostBuffer, 65, ref BytesRead, IntPtr.Zero);		// get buffer from device
		}

		///////////////////////////////////////////////////////////////////////////////////////////////
		public void sendViaUSB()
		{
			uint BytesWritten = 0;
			WriteFile(WriteHandleToUSBDevice, fromHostToDeviceBuffer, 65, ref BytesWritten, IntPtr.Zero);						// send buffer to device
		}

		///////////////////////////////////////////////////////////////////////////////////////////////
		//FUNCTION:	ReadFileManagedBuffer()
		//PURPOSE:	Wrapper function to call ReadFile()
		//
		//INPUT:	Uses managed versions of the same input parameters as ReadFile() uses.
		//
		//OUTPUT:	Returns boolean indicating if the function call was successful or not.
		//          Also returns data in the byte[] INBuffer, and the number of bytes read. 
		//
		//Notes:    Wrapper function used to call the ReadFile() function.  ReadFile() takes a pointer to an unmanaged buffer and deposits
		//          the bytes read into the buffer.  However, can't pass a pointer to a managed buffer directly to ReadFile().
		//          This ReadFileManagedBuffer() is a wrapper function to make it so application code can call ReadFile() easier
		//          by specifying a managed buffer.
		public unsafe bool ReadFileManagedBuffer(SafeFileHandle hFile, byte[] INBuffer, uint nNumberOfBytesToRead, ref uint lpNumberOfBytesRead, IntPtr lpOverlapped)
		{
			IntPtr pINBuffer = IntPtr.Zero;

			try
			{
				pINBuffer = Marshal.AllocHGlobal((int)nNumberOfBytesToRead);    //Allocate some unmanged RAM for the receive data buffer.

				if (ReadFile(hFile, pINBuffer, nNumberOfBytesToRead, ref lpNumberOfBytesRead, lpOverlapped))
				{
					Marshal.Copy(pINBuffer, INBuffer, 0, (int)lpNumberOfBytesRead);    //Copy over the data from unmanged memory into the managed byte[] INBuffer
					Marshal.FreeHGlobal(pINBuffer);
					return true;
				}
				else
				{
					Marshal.FreeHGlobal(pINBuffer);
					return false;
				}
			}
			catch
			{
				if (pINBuffer != IntPtr.Zero)
				{
					Marshal.FreeHGlobal(pINBuffer);
				} return (false);
			}
		}

	}		// end class
}		// end namespace

Best regards
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top