Enumerated2

Like Enumerated, except that this uses ROM to keep the descriptors — which does unfortunately mean repeating blocks of code, as pointers cannot be used to access the ROM.

Download compiled HEX firmware

#include <16f877.h>
 
//--------------------------------------------------------
// Setup PIC and CCS compiler
#fuses XT, PUT, NOWDT, NOPROTECT
#use delay(clock = 4000000)
 
#use rs232(baud = 19200, xmit = PIN_C6, rcv = PIN_C7, disable_ints)
// By using C6 and C7, we will make use of the 877's hardware USART abilities
 
#use fast_io(a)
#use standard_io(c)
#byte port_a = 5
set_tris_a(0xFF);	// All input
 
#use standard_io(b)
#byte port_d = 8
 
//--------------------------------------------------------
// Debug options
// 0 is most verbose, 1 less so etc
 
// Comment out the following line to bulid without debugging
// NB: This creates a LOT of warnings during compilation:
//     a "Code has no effect" for each DEBUGx statement
 
#define __DEBUGGING_ENABLED
 
#ifdef __DEBUGGING_ENABLED
	#define DEBUG0	if(DEBUG_LEVEL <= 0)DEBUGGED=1; if(DEBUG_LEVEL <= 0)printf
	#define DEBUG1	if(DEBUG_LEVEL <= 1)DEBUGGED=1; if(DEBUG_LEVEL <= 1)printf
	#define DEBUG2	if(DEBUG_LEVEL <= 2)DEBUGGED=1; if(DEBUG_LEVEL <= 2)printf
	#define DEBUG3	if(DEBUG_LEVEL <= 3)DEBUGGED=1; if(DEBUG_LEVEL <= 3)printf
	#define DEBUG4	if(DEBUG_LEVEL <= 4)DEBUGGED=1; if(DEBUG_LEVEL <= 4)printf
	#define DEBUG5	if(DEBUG_LEVEL <= 5)DEBUGGED=1; if(DEBUG_LEVEL <= 5)printf
	#define DEBUG6	if(DEBUG_LEVEL <= 6)DEBUGGED=1; if(DEBUG_LEVEL <= 6)printf
	#define DEBUG7	if(DEBUG_LEVEL <= 7)DEBUGGED=1; if(DEBUG_LEVEL <= 7)printf
#else
	#define DEBUG0
	#define DEBUG1
	#define DEBUG2
	#define DEBUG3
	#define DEBUG4
	#define DEBUG5
	#define DEBUG6
	#define DEBUG7
#endif
 
//--------------------------------------------------------
// Definitions
 
// Yellow LED
#define LED_N 			PIN_B7
 
// Constants
#define ON				1
#define OFF				0
 
#define D12_DATA		0
#define D12_COMMAND		1
 
// D12 pins
#define D12_A0			PIN_B6
#define D12_WR_N		PIN_B2
#define D12_RD_N		PIN_B1
#define D12_SUSPEND		PIN_B4
#define D12_INT_N		PIN_B0
 
// D12 Constants
#define D12_CTRL_BUFFER_SIZE	16	// (Bytes)
 
// D12 Endpoint indexes (for bitwise OR'ing with base commands)
#define CTRL_OUT		0
#define CTRL_IN			1
#define ENDPT1_OUT		2
#define ENDPT1_IN		3
#define ENDPT2_OUT		4
#define ENDPT2_IN		5
 
// D12 Commands
#define SET_ADDRESS			0xD0
#define SET_ENDPT_ENABLE	0xD8
#define SET_MODE			0xF3
#define SET_DMA				0xFB
#define READ_INT			0xF4
#define SELECT_ENDPT		0x00 // + endpoint index
#define READ_ENDPT_STATUS	0x40 // + endpoint index
#define READ_BUFFER			0xF0
#define WRITE_BUFFER		0xF0
#define SET_ENDPT_STATUS	0x40 // + endpoint index
#define ACK_SETUP			0xF1
#define CLEAR_BUFFER		0xF2
#define VALIDATE_BUFFER		0xFA
#define SEND_RESUME			0xF6
#define READ_FRAME_NUM		0xF5
 
// D12 Interrupt byte 1
#define INT_CTRL_OUT		0x01 // bit 0
#define INT_CTRL_IN			0x02 // bit 1
#define INT_ENDPT1_OUT		0x04 // bit 2
#define INT_ENDPT1_IN		0x08 // bit 3
#define INT_ENDPT2_OUT		0x10 // bit 4
#define INT_ENDPT2_IN		0x20 // bit 5
#define INT_BUS_RESET		0x40 // bit 6
#define INT_SUSPEND_CHANGE	0x80 // bit 7
 
// D12 Interrupt byte 2
#define DMA_EOT_INT		0x01 // bit 0
 
// D12 Last transaction status
#define STAT_XFER_SUCCESS	0x01	// bit 0 (1=Success)
#define STAT_ERROR			0x1E	// bits 1-4
#define STAT_SETUP			0x20	// bit 5 (1=Last packet has setup token)
#define STAT_DATA			0x40	// bit 6 (0/1 to indicate DATA0 / DATA1 tag of packet)
#define STAT_NOT_READ		0x80	// bit 7 (1=Previous status not read (i.e. missed))
 
// USB bmRequestTypes
#define REQTYPE_XFER_DIRECTION	0x80	// 0=OUT (Host to device), 1=IN (Device to host)
#define REQTYPE_CMD_TYPE		0x60	// 0=Standard 1=Class 3=Vendor
#define REQTYPE_RECIPIENT		0x1F	// 0=Device, 1=Interface, 2=Endpoint, 3=Other
 
// USB Standard request types
#define GET_STATUS_REQ			0x00
#define CLEAR_FEATURE_REQ		0x01
#define SET_FEATURE_REQ			0x03
#define SET_ADDRESS_REQ			0x05
#define GET_DESCRIPTOR_REQ		0x06
#define SET_DESCRIPTOR_REQ		0x07
#define GET_CONFIGURATION_REQ	0x08
#define SET_CONFIGURATION_REQ	0x09
#define GET_INTERFACE_REQ		0x0A
#define SET_INTERFACE_REQ		0x0B
#define SYNCH_FRAME_REQ			0x0C
 
// WHICH_DESCTIPOR values
#define DES_NULL			0x00
#define DES_DEVICE			0x01
#define DES_CONFIGURATION	0x02
#define DES_LANG_ID			0x03
#define DES_STRING1			0x04
#define DES_STRING2			0x05
#define DES_STRING3			0x06
 
//--------------------------------------------------------
// Global Variable Declarations
short DEBUGGED;				// Flags if a DEBUGx statement was executed
short CTRL_IN_JUST_FINISHED;// Used to determine if D12 CTRL_IN interrupt is because the buffer was (successfully) sent
unsigned char DEBUG_LEVEL;
unsigned char LOAD_INDEX;			// Used when sending descriptors
unsigned char LOAD_LENGTH;		// Because sometimes the host requests only part of a descriptor
unsigned char SET_ADDRESS_PENDING;
unsigned char WHICH_DESCRIPTOR;		// Descriptors are constants, so we can't use pointers and need to use
									// a switch statements in the CTRL_IN interrupt handler
 
//--------------------------------------------------------
// Structures
struct REQUEST {
	int8	bmRequestType;
	int8	bRequest;
	int16	wValue;
	int16	wIndex;
	int16	wLength;	// Data Phase's data length
};
 
//--------------------------------------------------------
// USB Descriptors
unsigned char const sDevice[] = {
	0x12,					//BYTE bLength
	0x01,					//BYTE bDescriptorType
	0x10,					//WORD (Lo) bcdUSB version supported
	0x01,					//WORD (Hi) bcdUSB version supported
	0xff,					//BYTE bDeviceClass
	0xff,					//BYTE bDeviceSubClass
	0xff,					//BYTE bDeviceProtocol
	D12_CTRL_BUFFER_SIZE,	//BYTE bMaxPacketSize (probably 16)
	0x04,					//WORD (Lo) idVendor
	0x71,					//WORD (Hi) idVendor
	0x11,					//WORD (Lo) idProduct, For Philips Hub mouse
	0x02,					//WORD (Hi) idProduct, For Philips Hub mouse
	0x00,					//WORD (Lo) bcdDevice
	0x00,					//WORD (Hi) bcdDevice
	0x01,					//BYTE iManufacturer
	0x02,					//BYTE iProduct
	0x00,					//BYTE iSerialNumber
	0x01 					//BYTE bNumConfigurations
};
 
unsigned char const sConfiguration[] = {
	0x09,					//BYTE bLength (Configuration descriptor)
	0x02,					//BYTE bDescriptorType //Assigned by USB
	0x22,					//WORD (Lo) wTotalLength
	0x00,					//WORD (Hi) wTotalLength
	0x01,					//BYTE bNumInterfaces
	0x01,					//BYTE bConfigurationValue
	0x00,					//BYTE iConfiguration
	0xa0,					//BYTE bmAttributes, Bus powered and remote wakeup
	0x05,					//BYTE MaxPower
 
	0x09,					//BYTE bLength (Interface descriptor)
	0x04,					//BYTE bDescriptionType, assigned by USB
	0x00,					//BYTE bInterfaceNumber
	0x00,					//BYTE bAlternateSetting
	0x01,					//BYTE bNumEndpoints, uses 1 endpoints
	0x03,					//BYTE bInterfaceClass, HID Class - 0x03
	0x01,					//BYTE bInterfaceSubClass
	0x01,					//BYTE bInterfaceProtocol
	0x00 					//BYTE iInterface
 
	0x09,					//BYTE bLength (HID Descriptor)
	0x21,					//BYTE bDescriptorType
	0x01,					//WORD (Lo) bcdHID
	0x00,					//WORD (Hi) bcdHID
	0x00,					//BYTE bCountryCode
	0x01,					//BYTE bNumDescriptors
	0x22,					//BYTE bReportDescriptorType
	0x00,					//WORD (Lo) wItemLength
	0x32,					//WORD (Hi) wItemLength
 
	0x07,					//BYTE bLength (Endpoint Descriptor)
	0x05,					//BYTE bDescriptorType, assigned by USB
	0x81,					//BYTE bEndpointAddress, IN endpoint, endpoint 1
	0x03,					//BYTE bmAttributes, Interrupt endpoint
	0x10,					//WORD (Lo) wMaxPacketSize
	0x00,					//WORD (Hi) wMaxPacketSize
	0x0A,					//Polling Time
}; //Interval
 
unsigned char const sLang_ID[] = {
	0x04,					// bLength
	0x03,					// bDescriptorType = String Desc
	0x09,					// wLangID (Lo) (Lang ID for English = 0x0409)
	0x04,					// wLangID (Hi) (Lang ID for English = 0x0409)
};
 
unsigned char const sString1[] = {
	0x76,					// bLength
	0x03					// bDescriptorType = String Desc
	// Noting that text is always unicode, hence the 'padding'
	'P', 00, 'h', 00, 'i', 00, 'l', 00, 'i', 00,
	' ', 00, '-', 00, '-', 00, 'p', 00, 's', 00,
	' ', 00, 'S', 00, 'e', 00, 'm', 00, 'i', 00,
	'c', 00, 'o', 00, 'n', 00, 'd', 00, 'u', 00,
	'c', 00, 't', 00, 'o', 00, 'r', 00, 's', 00,
	' ', 00, '-', 00, ' ', 00, 'A', 00, 's', 00,
	'i', 00, 'a', 00, ' ', 00, 'P', 00, 'r', 00,
	'o', 00, 'd', 00, 'u', 00, 'c', 00, 't', 00,
	' ', 00, 'I', 00, 'n', 00, 'n', 00, 'o', 00,
	'v', 00, 'a', 00, 't', 00, 'i', 00, 'o', 00,
	'n', 00, ' ', 00, 'C', 00, 'e', 00, 'n', 00,
	't', 00, 'r', 00, 'e', 00,
};
 
 
unsigned char const sString2[] = {
	0x46, 					// bLength
	0x03, 					// bDescriptorType = String Desc
	// Noting that text is always unicode, hence the 'padding'
	'P', 00, 'D', 00, 'I', 00, 'U', 00, 'S', 00,
	'B', 00, 'D', 00, '1', 00, '1', 00, ' ', 00,
	',', 00, ' ', 00, 'D', 00, '1', 00, '1', 00,
	' ', 00, 'F', 00, 'i', 00, 'r', 00, 'm', 00,
	'w', 00, 'a', 00, 'r', 00, 'e', 00, ' ', 00,
	'V', 00, 'e', 00, 'r', 00, 's', 00, 'i', 00,
	'o', 00, 'n', 00, ' ', 00, '1', 00
};
 
 
unsigned char const sString3[] = {
	0x08, 					// bLength
	0x03, 					// bDescriptorType = String Desc
	// Noting that text is always unicode, hence the 'padding'
	'X', 00, 'W', 00, 'L', 00
};
 
 
//--------------------------------------------------------
// Function prototypes
void Init_PIC();
void Init_D12();
void D12_Write(short, int);
void D12_Read(unsigned char, int);
void D12_Interrupt_Handler();
void D12_Handle_Ctrl_Out_EP();
void D12_Stall_Endpt(int8);
void D12_Standard_Request(struct REQUEST *pReq);
void D12_Get_Descriptor(struct REQUEST *pReq);
void D12_Send_Null_Packet(int8);
void D12_Set_Address(struct REQUEST *pReq);
#SEPARATE void D12_Transaction_Error(int8);
#SEPARATE void Debug_D12_Request(struct REQUEST *pReq);
 
//--------------------------------------------------------
// Entry point
void main(void){
 
	DEBUGGED = 0;
	Init_PIC();					// Put pins in known state, reset D12 etc
 
	delay_ms(1);
 
	// No need to init the D12, as it will trigger a bus reset interrupt as soon
	// as it is powered / connects to the USB bus (not too sure which though)
 
	if(input(D12_INT_N) == 0) D12_Interrupt_Handler();
 
	while(TRUE);				// Wait for interrupt
}
 
//--------------------------------------------------------
// Used for passing commands or data to the PDIUSBD12
void D12_Write(short type, int data)
{
	int8 i;
	switch(type)
	{
		case D12_DATA:
		case D12_COMMAND:
			set_tris_d(0x00);		// Set bus to output mode
			output_high(D12_RD_N);	// Ensure we don't conflict with RD_N
 
			if(type == D12_COMMAND)
				output_high(D12_A0);
			else
				output_low(D12_A0);
 
			port_d = data;			// Setup bus
			//delay_ms(1);			// Data settling time
			i = 8;
			while(i--);				// Settling time (in PIC cycles)
			output_low(D12_WR_N);	// strobe for at least 20ns
			output_high(D12_WR_N);
 
			if(type == D12_COMMAND)
				output_low(D12_A0);
			break;
		default:
			//DEBUG7("Error in D12_Write(), unknown type: 0x%x!\r\n", type);
			//DEBUG7("Expecting one of:\r\n\t 0x%x\r\n\t0x%x\r\n", D12_COMMAND, D12_DATA);
	}
}
 
//--------------------------------------------------------
// Used for reading data from the PDIUSBD12
void D12_Read(unsigned char* buffer, int reads)
{
	int i;
 
	set_tris_d(0xFF);			// Set bus to intput mode
	for(i = 0; i<reads; i++)
	{
		output_low(D12_RD_N);
		buffer[i] = port_d;		// Latch in the bus
		output_high(D12_RD_N);
	}
}
 
//--------------------------------------------------------
// FIXME: Probably want to save some registers when handling
//        this interrupt, as it takes quite a long time.
#INT_EXT
void D12_Interrupt_Handler()
{
	unsigned char buffer[2], endpt_int, other_int;
 
	// Loop in case another interrupt is triggered while we handle this one
	while(! input(D12_INT_N)){
		// Don't add newlines if we've not sent any data to the terminal
		if(DEBUGGED > 0)printf("\r\n", DEBUGGED);
		DEBUGGED = 0;
 
		D12_Write(D12_COMMAND, READ_INT);
		D12_Read(buffer, 2);
		endpt_int = buffer[0];
		other_int = buffer[1];
 
		DEBUG0("IR=%x,%x ", endpt_int, other_int);
 
		if (endpt_int & INT_BUS_RESET) {
			DEBUG7("BR ");
			// D12 Firmware programming guide recommends using a flag for this... ahh well
			Init_D12();	// Reset D12 settings (not a chip reset)
		} else
		if (endpt_int & INT_SUSPEND_CHANGE) {
			DEBUG1("SC ");
		} else
		if(endpt_int & INT_CTRL_OUT) {
			// Control Out Endpoint interrupt
			DEBUG3("CO ");
			D12_Handle_Ctrl_Out_EP();
		} else
		if (endpt_int & INT_CTRL_IN) {
			DEBUG3("CI ");
 
			// Clear interrupt
			D12_Write(D12_COMMAND, READ_ENDPT_STATUS + CTRL_IN);
			D12_Read(buffer, 1);
			DEBUG1("LT=%x ", buffer[0]);
 
			if(SET_ADDRESS_PENDING){
				// Acknowledge token by replying with a null data packet
				D12_Send_Null_Packet(CTRL_IN);
 
				D12_Write(D12_COMMAND, SET_ADDRESS);
				D12_Write(D12_DATA, SET_ADDRESS_PENDING);	// This contains the address to be set
 
				SET_ADDRESS_PENDING = FALSE;
				CTRL_IN_JUST_FINISHED = TRUE;	// Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent
				DEBUG3("AS ");
			} else if (WHICH_DESCRIPTOR) {
				unsigned char DataLen;
				DEBUG5("DR ");
 
				switch(WHICH_DESCRIPTOR){
					case DES_DEVICE:
						DEBUG5("DDR ");
						// Smaller of "length of data to send" and "control buffer size"
						DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX;
						DEBUG0("DataLen=%x ", DataLen);
 
						D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN);
						D12_Write(D12_COMMAND, WRITE_BUFFER);
						D12_Write(D12_DATA, 0x00);		// First byte is reserved
						D12_Write(D12_DATA, DataLen);	// Num of data bytes
 
						DataLen += LOAD_INDEX;
						for(; LOAD_INDEX<DataLen; LOAD_INDEX++)
						{
							if(DEBUG_LEVEL <= 5)printf("%x ", sDevice[LOAD_INDEX]);
							D12_Write(D12_DATA, sDevice[LOAD_INDEX]);
						}
						D12_Write(D12_COMMAND, VALIDATE_BUFFER);	// Mark the buffer as ready to go!
 
						// Check if we're finished
						if(LOAD_INDEX == sizeof(sDevice))WHICH_DESCRIPTOR = FALSE;
						CTRL_IN_JUST_FINISHED = TRUE;	// Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent
						DEBUG2("DS ");
					break;
 
					case DES_CONFIGURATION:
						DEBUG5("CDR ");
						// Smaller of "length of data to send" and "control buffer size"
						DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX;
						DEBUG0("DataLen=%x ", DataLen);
 
						D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN);
						D12_Write(D12_COMMAND, WRITE_BUFFER);
						D12_Write(D12_DATA, 0x00);		// First byte is reserved
						D12_Write(D12_DATA, DataLen);	// Num of data bytes
 
						DataLen += LOAD_INDEX;
						for(; LOAD_INDEX<DataLen; LOAD_INDEX++)
						{
							DEBUG5("%x ", sConfiguration[LOAD_INDEX]);
							D12_Write(D12_DATA, sConfiguration[LOAD_INDEX]);
						}
						D12_Write(D12_COMMAND, VALIDATE_BUFFER);	// Mark the buffer as ready to go!
 
						// Check if we're finished
						if(LOAD_INDEX == sizeof(sConfiguration))WHICH_DESCRIPTOR = FALSE;
						CTRL_IN_JUST_FINISHED = TRUE;	// Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent
						DEBUG2("DS ");
					break;
 
					case DES_LANG_ID:
						DEBUG5("SDR ");
						// Smaller of "length of data to send" and "control buffer size"
						DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX;
						DEBUG0("DataLen=%x ", DataLen);
 
						D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN);
						D12_Write(D12_COMMAND, WRITE_BUFFER);
						D12_Write(D12_DATA, 0x00);		// First byte is reserved
						D12_Write(D12_DATA, DataLen);	// Num of data bytes
 
						DataLen += LOAD_INDEX;
						for(; LOAD_INDEX<DataLen; LOAD_INDEX++)
						{
							DEBUG5("%x ", sLang_ID[LOAD_INDEX]);
							D12_Write(D12_DATA, sLang_ID[LOAD_INDEX]);
						}
						D12_Write(D12_COMMAND, VALIDATE_BUFFER);	// Mark the buffer as ready to go!
 
						// Check if we're finished
						if(LOAD_INDEX == sizeof(sLang_ID))WHICH_DESCRIPTOR = FALSE;
						CTRL_IN_JUST_FINISHED = TRUE;	// Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent
						DEBUG2("DS ");
					break;
 
					case DES_STRING1:
						DEBUG5("SDR ");
						// Smaller of "length of data to send" and "control buffer size"
						DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX;
						DEBUG0("DataLen=%x ", DataLen);
 
						D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN);
						D12_Write(D12_COMMAND, WRITE_BUFFER);
						D12_Write(D12_DATA, 0x00);		// First byte is reserved
						D12_Write(D12_DATA, DataLen);	// Num of data bytes
 
						DataLen += LOAD_INDEX;
						for(; LOAD_INDEX<DataLen; LOAD_INDEX++)
						{
							DEBUG5("%x ", sString1[LOAD_INDEX]);
							D12_Write(D12_DATA, sString1[LOAD_INDEX]);
						}
						D12_Write(D12_COMMAND, VALIDATE_BUFFER);	// Mark the buffer as ready to go!
 
						// Check if we're finished
						if(LOAD_INDEX == sizeof(sString1))WHICH_DESCRIPTOR = FALSE;
						CTRL_IN_JUST_FINISHED = TRUE;	// Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent
						DEBUG2("DS ");
					break;
 
					case DES_STRING2:
						DEBUG5("SDR ");
						// Smaller of "length of data to send" and "control buffer size"
						DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX;
						DEBUG0("DataLen=%x ", DataLen);
 
						D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN);
						D12_Write(D12_COMMAND, WRITE_BUFFER);
						D12_Write(D12_DATA, 0x00);		// First byte is reserved
						D12_Write(D12_DATA, DataLen);	// Num of data bytes
 
						DataLen += LOAD_INDEX;
						for(; LOAD_INDEX<DataLen; LOAD_INDEX++)
						{
							DEBUG5("%x ", sString2[LOAD_INDEX]);
							D12_Write(D12_DATA, sString2[LOAD_INDEX]);
						}
						D12_Write(D12_COMMAND, VALIDATE_BUFFER);	// Mark the buffer as ready to go!
 
						// Check if we're finished
						if(LOAD_INDEX == sizeof(sString2))WHICH_DESCRIPTOR = FALSE;
						CTRL_IN_JUST_FINISHED = TRUE;	// Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent
						DEBUG2("DS ");
					break;
 
					case DES_STRING3:
						DEBUG5("SDR ");
						// Smaller of "length of data to send" and "control buffer size"
						DataLen = (LOAD_LENGTH - LOAD_INDEX > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_LENGTH - LOAD_INDEX;
						DEBUG0("DataLen=%x ", DataLen);
 
						D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN);
						D12_Write(D12_COMMAND, WRITE_BUFFER);
						D12_Write(D12_DATA, 0x00);		// First byte is reserved
						D12_Write(D12_DATA, DataLen);	// Num of data bytes
 
						DataLen += LOAD_INDEX;
						for(; LOAD_INDEX<DataLen; LOAD_INDEX++)
						{
							DEBUG5("%x ", sString3[LOAD_INDEX]);
							D12_Write(D12_DATA, sString3[LOAD_INDEX]);
						}
						D12_Write(D12_COMMAND, VALIDATE_BUFFER);	// Mark the buffer as ready to go!
 
						// Check if we're finished
						if(LOAD_INDEX == sizeof(sString3))WHICH_DESCRIPTOR = FALSE;
						CTRL_IN_JUST_FINISHED = TRUE;	// Unless otherwise flagged, the next CTRL_IN interrupt just means the data was sent
						DEBUG2("DS ");
					break;
 
					default:
						DEBUG2("!DS(%x)", WHICH_DESCRIPTOR);
					break;
				}
 
			} else if (CTRL_IN_JUST_FINISHED) {
				// This interrupt is just the D12 telling us it's emptied its buffer (i.e. sent it to the host)
				CTRL_IN_JUST_FINISHED = FALSE;
			} else {
				// Stall this endpoint (indicating we cannot handle the request)
				 D12_Stall_Endpt(CTRL_IN);
			}
		} else
		// INT_ENDPT1_OUT
		if (endpt_int & INT_ENDPT1_IN) {
			DEBUG2("1I ");
			D12_Write(D12_COMMAND, READ_ENDPT_STATUS + ENDPT1_IN);	// Read last transaction status, for endpoint 1 IN
			D12_Read(buffer, 1);					// Clears the IN interrupt
			DEBUG2("LT=%x ", buffer[0]);
		}
		// INT_ENDPT2_OUT (Main OUT)
		// INT_ENDPT2_IN (Main IN)
	}
}
 
//--------------------------------------------------------
// Setups the hardware at its most basic level
void Init_PIC(){
	output_low(LED_N);			// Turn on the yellow LED
 
	set_tris_b(0x01);	//PIN_B1 (D12's INT) is input, the rest are output.
	set_tris_d(0x00);	//All output
	port_d = 0xFF;		//Set bus high, useful for checking the ribbon has not come loose
 
	output_high(D12_RD_N);
	output_high(D12_WR_N);
 
	output_low(D12_A0);			// Indicates bus is for data
	output_low(D12_SUSPEND);	// Prevent D12 from going into suspend
 
	disable_interrupts(GLOBAL);	// Stop interrupts from interrupting us while we setup ;)
 
	ext_int_edge(H_TO_L);		// Set up when to trigger
	enable_interrupts(INT_EXT);	// Enable external interrupts (connected to the D12's INT_N)
	clear_interrupt(INT_EXT);	// Remove pending interrupts
	enable_interrupts(GLOBAL);	// Enable all interrupts
 
	DEBUG_LEVEL = port_a & 0x07;// Read DIP switches (3 lower digits only)
	DEBUG7("\r\n\r\n%d ", DEBUG_LEVEL);
}
 
//--------------------------------------------------------
// Takes the D12 out of reset and connects it to the USB bus
void Init_D12(){
	SET_ADDRESS_PENDING = 0;
	WHICH_DESCRIPTOR = 0;
	LOAD_INDEX = 0;
 
	DEBUG0("_SAE ");
	D12_Write(D12_COMMAND, SET_ADDRESS);
	D12_Write(D12_DATA, 0x00 | 0x80);
 
	DEBUG0("_SEE ");
	D12_Write(D12_COMMAND, SET_ENDPT_ENABLE);
	D12_Write(D12_DATA, 0x01);
 
	DEBUG0("_SM ");
	D12_Write(D12_COMMAND, SET_MODE);
	D12_Write(D12_DATA, 0x1E);	// Non-ISO, Softconnect, Interrupt for all, Clock running, no Lazyclock
	D12_Write(D12_DATA, 0x0B);	// Clock 4MHz, Set-to-one isn't, no SOF interrupts
}
 
//--------------------------------------------------------
// Check for a SETUP token, and act upon it
void D12_Handle_Ctrl_Out_EP() {
 
	unsigned char buffer[2];
	unsigned char data[D12_CTRL_BUFFER_SIZE];
	struct REQUEST *pReq;		// Will be pointed to to data[] when appropriate
	int i;
 
	D12_Write(D12_COMMAND, READ_ENDPT_STATUS + CTRL_OUT);
	D12_Read(buffer, 1);
	DEBUG3("LT=%x ", buffer[0]);
 
	if(buffer[0] & STAT_NOT_READ){	// Previous status not read
		// Nothing yet ;)
	}
 
	if(buffer[0] & STAT_XFER_SUCCESS){
		if(buffer[0] & STAT_SETUP){	// Setup token
 
			D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_OUT);
			D12_Read(data, 1);
			DEBUG2("SE=%x ", data[0]);
 
			D12_Write(D12_COMMAND, READ_BUFFER);
			D12_Read(data, D12_CTRL_BUFFER_SIZE);
			DEBUG2("DL=%x ", data[1]);	// Note that [0] is reserved, so [1] contains the data length
 
			// Acknowledge that we like this (NB CTRL_OUT is already selected)
			D12_Write(D12_COMMAND, ACK_SETUP);
			D12_Write(D12_COMMAND, CLEAR_BUFFER);
 
			// Prevent previous data from being sent (need to ack_setup to re-enable clear buffer)
			D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_IN);
			D12_Write(D12_COMMAND, ACK_SETUP);
			D12_Write(D12_COMMAND, CLEAR_BUFFER);
 
			if(data[1] == 0x08){	// Valid setup token is 8 bytes
				for(i=2; i<10; i++){
					DEBUG0("%x ", data[i]);
				}
				pReq = (struct REQUEST *) &data[2];	// [0] is reserved, [1] is data length, so [2] is actual data
 
				// Output some debugging info
				Debug_D12_Request(pReq);
 
				switch((pReq->bmRequestType & REQTYPE_CMD_TYPE) >> 5){
					// Standard request
					case 0x00:
						DEBUG2("SREQ ");
						D12_Standard_Request(pReq);
					break;
 
					// Class request
					case 0x01:
						DEBUG2("CREQ ");
					break;
 
					// Endpoint request
					case 0x02:
						DEBUG2("EREQ ");
					break;
 
					// Unsupported
					default:
						DEBUG4("\x07");		// Bell character (^G)
						DEBUG2("?REQ=%x ", (pReq->bmRequestType & REQTYPE_CMD_TYPE) >> 5);
						// Stall this endpoint (indicating we cannot handle the request)
						D12_Stall_Endpt(CTRL_OUT);
					break;
				}
			} else {
				// Setup token is an invalid length
				D12_Stall_Endpt(CTRL_OUT);
			}
		}
	} else
	if (buffer[0] & STAT_ERROR)		// Last transaction wasn't successful
	{
		D12_Transaction_Error(buffer[0] & STAT_ERROR);
	}
}
 
//--------------------------------------------------------
// Stalls an enpoint, so the D12 will return STALL to the host,
// which usually indicates we don't understand / support the host's
// request
void D12_Stall_Endpt(int8 ENDPT)
{
	DEBUG5("S_");
	switch(ENDPT){
		case 0: DEBUG5("CO "); break;
		case 1: DEBUG5("CI "); break;
		case 2: DEBUG5("EO "); break;
		case 3: DEBUG5("EI "); break;
		case 4: DEBUG5("MO "); break;
		case 5: DEBUG5("MI "); break;
		default: DEBUG5("?(%x) ", ENDPT); break;
	}
	D12_Write(D12_COMMAND, SET_ENDPT_STATUS + ENDPT);
	D12_Write(D12_DATA, 0x01);
}
 
//--------------------------------------------------------
// Handle standard USB requests, such as those encountered in
// SETUP tokens
void D12_Standard_Request(struct REQUEST *pReq)
{
	switch(pReq->bRequest){
		case GET_STATUS_REQ:
			DEBUG5("Get_Staus ");
		break;
 
		case CLEAR_FEATURE_REQ:
			DEBUG5("Clear_Feature ");
		break;
 
		case SET_FEATURE_REQ:
			DEBUG5("Set_feature ");
		break;
 
		case SET_ADDRESS_REQ:
			DEBUG5("Set_Address ");
			D12_Set_Address(pReq);
		break;
 
		case GET_DESCRIPTOR_REQ:
			DEBUG5("Get_Descriptor ");
			D12_Get_Descriptor(pReq);
		break;
 
		case SET_DESCRIPTOR_REQ:
			DEBUG5("Set_Descriptor ");
		break;
 
		case GET_CONFIGURATION_REQ:
			DEBUG5("Get_Configuration ");
		break;
 
		case SET_CONFIGURATION_REQ:
			DEBUG5("Set_Configuration ");
		break;
 
		case GET_INTERFACE_REQ:
			DEBUG5("Get_Interface ");
		break;
 
		case SET_INTERFACE_REQ:
			DEBUG5("Set_Interface ");
		break;
 
		case SYNCH_FRAME_REQ:
			DEBUG5("Synch_Frame ");
		break;
 
		default:
			DEBUG7("\x07");		// Bell character (^G)
			DEBUG5("?SREQ=%x ", pReq->bRequest);
		break;
	}
}
 
//--------------------------------------------------------
// Service the host's request for a descriptor
void D12_Get_Descriptor(struct REQUEST *pReq){
 
	unsigned int16 ReqDataLen;
	short supported = 1;
	ReqDataLen = pReq->wLength;
 
	switch (pReq->wValue >> 8){	// We only care about the high byte
		// Device Descriptor
		case 0x0001:
			DEBUG3("DDR ");
			// Don't transmit more than we need to
			if ( ReqDataLen > (unsigned int16) sizeof(sDevice) ){
				ReqDataLen = sizeof(sDevice);
			}
			WHICH_DESCRIPTOR = DES_DEVICE;
		break;
 
		// Configuration Descriptor
		case 0x0002:
			DEBUG3("CDR ");
			// Don't transmit more than we need to
			if ( ReqDataLen > (unsigned int16) sizeof(sConfiguration) ){
				ReqDataLen = sizeof(sConfiguration);
			}
			WHICH_DESCRIPTOR = DES_CONFIGURATION;
		break;
 
		// String descriptor
		case 0x0003:
			DEBUG3("SDR=%x ", pReq->wValue & 0x0F);
			// Now we need to check which string was requested (low byte)
			switch(pReq->wValue & 0x0F){
				// LANG_ID string descriptor (what language strings we can return)
				case 0:
					WHICH_DESCRIPTOR = DES_LANG_ID;
					// Don't transmit more than we need to
					if ( ReqDataLen > (unsigned int16) sizeof(sLang_ID) ){
						ReqDataLen = sizeof(sLang_ID);
					}
				break;
 
				case 1:
					WHICH_DESCRIPTOR = DES_STRING1;
					// Don't transmit more than we need to
					if ( ReqDataLen > (unsigned int16) sizeof(sString1) ){
						ReqDataLen = sizeof(sString1);
					}
				break;
 
				case 2:
					WHICH_DESCRIPTOR = DES_STRING2;
					// Don't transmit more than we need to
					if ( ReqDataLen > (unsigned int16) sizeof(sString2) ){
						ReqDataLen = sizeof(sString2);
					}
				break;
 
				case 3:
					WHICH_DESCRIPTOR = DES_STRING3;
					// Don't transmit more than we need to
					if ( ReqDataLen > (unsigned int16) sizeof(sString3) ){
						ReqDataLen = sizeof(sString3);
					}
				break;
 
				// Unknown string descriptor
				default:
					DEBUG4("\x07");		// Bell character (^G)
					DEBUG4("?SDR ");
					supported = 0;
				break;
			}
		break;
 
		// Unsupported
		default:
			supported = 0;
			DEBUG4("\x07");		// Bell character (^G)
			DEBUG5("?DR=%x ",(pReq->wValue >> 8));
			// Not sure which endpoint would need to be stalled..
			// presumably the one the request came from (CTRL_OUT)
			D12_Stall_Endpt(CTRL_OUT);
		break;
   }
 
   if(supported){
	   // Note that if our descriptor is larger then what the host requested,
	   //  we only send what we can, it's up to the host to make another request,
	   //  with a larger data phase (hence we will start again from the beginning)
	   // However, if our descriptor is larger than the D12's CTRL_OUT buffer, then
	   //  we need to send multiple packets, filling the buffer on each CTRL_IN interrupt
	   LOAD_INDEX = 0;	// Make sure we start reading our descriptor array from the beginning
	   LOAD_LENGTH = ReqDataLen;
  }
}
 
//--------------------------------------------------------
// When an error code is encountered from a 'Read Last Transaction Command'
// this function is called to clean up the mess
 
#SEPARATE void D12_Transaction_Error(int8 error){
	DEBUG5("!LT=%x ", error);
	switch (error)
	{
		case 0x02 : //0001 PID Encoding Error
		break;
		case 0x04 : //0010 PID Unknown
		break;
		case 0x06 : //0011 Unexpected packet
		break;
		case 0x08 : //0100 Token CRC Error
		break;
		case 0x0A : //0101 Data CRC Error
		break;
		case 0x0C : //0110 Time out Error
		break;
		case 0x0E : //0111 Never happens
		break;
		case 0x10 : //1000 Unexpected End of Packet
		break;
		case 0x12 : //1001 Sent or received NAK
		break;
		case 0x14 : //1010 Sent Stall, token received Endpt Stalled
		break;
		case 0x16 : //1011 Overflow Error
		break;
		case 0x1A : //1101 BitStuff Error
		break;
		case 0x1E : //1111 Wrong DATA PID
		break;
		default :
			DEBUG7("\x07");		// Bell character (^G)
			DEBUG5("?LT=%x ", error);
		break;
	}
}
 
//--------------------------------------------------------
// Output debugging info about a USB request
 
#SEPARATE void Debug_D12_Request(struct REQUEST *pReq){
 
	DEBUG4("DIR=");
	if(pReq->bmRequestType & REQTYPE_XFER_DIRECTION){
		DEBUG4("I ");
	} else {
		DEBUG4("O ");
	}
 
	DEBUG4("TO=");
	switch(pReq->bmRequestType & REQTYPE_RECIPIENT){
		// Device
		case 0x00:
			DEBUG4("D ");
		break;
 
		// Interface
		case 0x01:
			DEBUG4("I ");
		break;
 
		// Endpoint
		case 0x02:
			DEBUG4("E ");
		break;
 
		// Other
		case 0x03:
			DEBUG4("? ");
		break;
 
		// Unsupported
		default:
			DEBUG7("\x07");		// Bell character (^G)
			// Stall this endpoint (indicating we cannot handle the request)
			D12_Stall_Endpt(CTRL_OUT);
		break;
	}
 
	DEBUG4("wV=%Lx ", pReq->wValue);
	DEBUG4("wI=%Lx ", pReq->wIndex);
	DEBUG4("wL=%Lx ", pReq->wLength);
}
 
//--------------------------------------------------------
// Responds to the Set_Address request of the host, and then
// sets the D12 address (NB: The address is changed AFTER the
// we respond to the host)
void D12_Set_Address(struct REQUEST *pReq){
	SET_ADDRESS_PENDING = (pReq->wValue | 0x80);
	DEBUG3("SAEP ");
}
 
//--------------------------------------------------------
// Send a zero-length packet to the selected endpoint
// Useful for empty data stages in setup transactions, as well
// as signalling the end of a stream when the last packet was
// full (i.e. don't let the host assume there is no more data,
// tell it!)
void D12_Send_Null_Packet(int8 ENDPT) {
	D12_Write(D12_COMMAND, SELECT_ENDPT + ENDPT);
	D12_Write(D12_COMMAND, WRITE_BUFFER);
	D12_Write(D12_DATA, 0);				// First packet is reserved
	D12_Write(D12_DATA, 0);				// Data length (zero-length packet)
	D12_Write(D12_COMMAND, VALIDATE_BUFFER);
 
	DEBUG3("Z ");
}