User Tools

Site Tools


project:usb:code:joystick

Joystick

This is my near-complete code, I don't think I'll have any time to actually “finish” it, so I'll outline it's shortcomings here:

  • A HID-Class Get_Report request has never sent non-zero data in my experiments, but this may be because Get_Report and Set_Report were being issued at the same time
  • Send_Report() does not appear to handle multi-transfers properly (i.e. when sending reports larger than the endpoint buffer), this may be due to some fudging of the data on my part as there was not enough RAM to do a proper test of this.
  • Similarly, multi-transfers for Set_Report requests have not been tested very thoroughly. The little testing done was encouraging and I'm quite confident about the code which handles it.
  • X/Y axis of joystick don't seem to be controlled properly. I am sure the error lies in the report descriptor as I had this working before I changed that, and have not altered the code that produces and sends random data.

Download compiled HEX firmware

#include <16f877.h>
#include <STDLIB.H>	// Gives us RAND()
 
//--------------------------------------------------------
// Setup PIC and CCS compiler
#fuses XT, PUT, NOWDT, NOPROTECT
#use delay(clock = 4000000)
#case
 
// By using C6 and C7, we will make use of the 877's hardware USART abilities
#use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5, stream=DEBUG)
 
#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) Control buffer
#define D12_MAIN_BUFFER_SIZE	64	// (Bytes) Endpoint 2
 
// 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
 
// USB HID Class request types
#define GET_REPORT		0x01
#define GET_IDLE		0x02
#define GET_PROTOCOL	0x03
#define SET_REPORT		0x09
#define SET_IDLE		0x0A
#define SET_PROTOCOL	0x0B
 
// STATE
#define STATE_DEFAULT		0x00
#define STATE_ADDRESSED		0x01
#define STATE_CONFIGURED	0x02
 
//--------------------------------------------------------
// Structures and Enumerations
struct REQUEST {
	int8	bmRequestType;
	int8	bRequest;
	int16	wValue;
	int16	wIndex;
	int16	wLength;	// Data Phase's data length
};
 
struct REPORT {
	int8	buttons;
	int8	x;
	int8	y;
};
 
//--------------------------------------------------------
// Global Variable Declarations
short DEBUGGED;						// Flags if a DEBUGx statement was executed
short CTRL_IN_BUSY;					// Used to determine if D12 CTRL_IN interrupt is because the buffer was (successfully) sent
short CTRL_IN_SEND_REPORT;			// Flags if we need to send a report
unsigned char DEBUG_LEVEL;			// Set by DIP switches on power up, and determines how verbose debugging is, via DEBUGx statements
unsigned char LOAD_CIN_OFFSET;		// Used when sending descriptors via CTRL_IN
unsigned int16 LOAD_CIN_LENGTH;
unsigned char REPORT_OUT[2];
unsigned char LOAD_COUT_OFFSET;		// Used when receiving reports via CTRL_OUT.
unsigned int16 LOAD_COUT_LENGTH;
unsigned char SEND_REPORT_OFFSET;	// Used when sending reports (any endpoint, assumes never sending more than one at a time)
unsigned char SET_ADDRESS_PENDING;	// Flags the next CTRL_IN handler to send a null packet and then change the D12 address
unsigned char CONFIGURATION;		// Indicates the selected configuration
unsigned char STATE;				// USB device state. One of {Default, Addressed, Configured}
unsigned char ADDRESS;				// Current device address on USB bus (used in state checks for standard requests)
struct REPORT REPORT_IN;
 
//--------------------------------------------------------
// USB Descriptors
 
// Constants are stored in ROM, and hence cannot be accessed via pointers.
// Instead of having to duplicate code for each descriptor, we instead
// store them in a large block and 'parse' this block when a descriptor
// is requested.
unsigned char const DESCRIPTORS[] = {
// Device
	0x12,					//BYTE bLength
	0x01,					//BYTE bDescriptorType
	0x10,					//WORD (Lo) bcdUSB version supported
	0x01,					//WORD (Hi) bcdUSB version supported
	0x00,					//BYTE bDeviceClass
	0x00,					//BYTE bDeviceSubClass
	0x00,					//BYTE bDeviceProtocol
	D12_CTRL_BUFFER_SIZE,	//BYTE bMaxPacketSize (probably 16 bytes)
	0x25,					//WORD (Lo) idVendor (Lakeview Research (of USB Complete))
	0x09,					//WORD (Hi) idVendor (Lakeview Research (of USB Complete))
	0x34,					//WORD (Lo) idProduct (for compatability with HID Class app)
	0x12,					//WORD (Hi) idProduct (for compatability with HID Class app)
	0x88,					//WORD (Lo) bcdDevice
	0x02,					//WORD (Hi) bcdDevice
	0x01,					//BYTE iManufacturer
	0x02,					//BYTE iProduct
	0x03,					//BYTE iSerialNumber
	0x01 					//BYTE bNumConfigurations
 
// Configuration & associated subdescriptors
	// Configuration
	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
	0x32,					//BYTE MaxPower, 100mA
 
	// Interface
	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
	0x00,					//BYTE bInterfaceSubClass
	0x00,					//BYTE bInterfaceProtocol
	0x00 					//BYTE iInterface
 
	// HID
	0x09,					//BYTE bLength (HID Descriptor)
	0x21,					//BYTE bDescriptorType, assigned by USB
	0x10,					//WORD (Lo) bcdHID
	0x01,					//WORD (Hi) bcdHID
	0x00,					//BYTE bCountryCode
	0x01,					//BYTE bNumDescriptors
	0x22,					//BYTE bReportDescriptorType
	0x3D,					//WORD (Lo) wItemLength
	0x00,					//WORD (Hi) wItemLength
 
	// Endpoint
	0x07,					//BYTE bLength (Endpoint Descriptor)
	0x05,					//BYTE bDescriptorType, assigned by USB
	0x82,					//BYTE bEndpointAddress, IN endpoint, endpoint 1
	0x03,					//BYTE bmAttributes, Interrupt endpoint
	0x10,					//WORD (Lo) wMaxPacketSize
	0x00,					//WORD (Hi) wMaxPacketSize
	0xFF,					//BYTE bInterval
 
// Lang_ID (String0)
	0x04,					// bLength
	0x03,					// bDescriptorType = String Desc
	0x09,					// wLangID (Lo) (Lang ID for English = 0x0409)
	0x04,					// wLangID (Hi) (Lang ID for English = 0x0409)
 
// String1
	28+2,						// bLength
	0x03					// bDescriptorType = String Desc
	// Noting that text is always unicode, hence the 'padding'
	'R', 00, 'o', 00, 'b', 00, 'e', 00, 'r', 00,
	't', 00, ' ', 00, 'M', 00, 'e', 00, 'e', 00,
	'r', 00, 'm', 00, 'a', 00, 'n', 00,
 
// String2
	22+2, 					// bLength
	0x03, 					// bDescriptorType = String Desc
	// Noting that text is always unicode, hence the 'padding'
	'R', 00, 'o', 00, 'b', 00, '\'', 00, 's', 00,
	' ', 00, 'G', 00, 'i', 00, 'z', 00, 'm', 00,
	'o', 00,
 
// String3
	28+2, 					// bLength
	0x03, 					// bDescriptorType = String Desc
	// Noting that text is always unicode, hence the 'padding'
	'L', 00, 'i', 00, 'm', 00, 'i', 00, 't', 00,
	'e', 00, 'd', 00, 'E', 00, 'd', 00, 'i', 00,
	't', 00, 'i', 00, 'o', 00, 'n', 00,
 
// Report, NOTE that bLength and bDescriptorType are NOT part of the report descriptor,
// and have been addded so that Get_Descriptor() can parse this char array.
// !! Remember to update wItemLength (which is in Hex) in the HID Descriptor !!
	61+2,	// bLength			(NOT PART OF DESCRIPTOR!)
	0x22,	// bDescriptorType	(NOT PART OF DESCRIPTOR!)
 
    // Report descriptor proper
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x04,                    // USAGE (Joystick)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x75, 0x05,                    //     REPORT_SIZE (5)
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
    0x06, 0x00, 0xff,              //     USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    //     USAGE (Vendor Usage 1)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x91, 0x02,                    //     OUTPUT (Data,Var,Abs)
    0xc0,                          //   END_COLLECTION
    0xc0                           // END_COLLECTION
};
 
//--------------------------------------------------------
// Function prototypes
void Init_PIC();
void Init_D12();
void D12_Write(unsigned char, int);
void D12_Read(unsigned char, int);
void D12_Interrupt_Handler();
#SEPARATE void Handle_Ctrl_Out_EP();
#SEPARATE void Stall_Endpt(int8);
void Standard_Request(struct REQUEST *pReq);
void Get_Descriptor(struct REQUEST *pReq);
void Send_Null_Packet(int8);
void Set_Address(struct REQUEST *pReq);
void Set_Configuration(struct REQUEST *pReq);
void Class_Request(struct REQUEST *pReq);
#SEPARATE void Handle_Ctrl_In_EP();
#SEPARATE void Transaction_Error(int8);
#SEPARATE void Debug_Request(struct REQUEST *pReq);
short Send_Report(int8);
 
//--------------------------------------------------------
// Entry point
void main(void){
 
	DEBUGGED = 0;
	Init_PIC();			// Put pins in known state, reset D12 etc
 
	REPORT_IN.x = 0;
	REPORT_IN.y = 0;
	REPORT_IN.buttons = 0;
 
	// 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();
 
	// Random data, or GBA controlled?
	if(port_a & 0x08){	// PIN_A3
		while(TRUE){
			REPORT_IN.x = rand();
			REPORT_IN.y = rand();
			REPORT_IN.buttons = rand();
		}
	} else {
		#use rs232(baud = 9600, xmit = PIN_C6, rcv = PIN_C7)
		DEBUG7("GBA Controller enabled!");
		#use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5)
 
		enable_interrupts(INT_RDA);	// Enable RecieveDataAvailable (hardware USART double buffer has data)
		while(TRUE);		// Wait for (USART / D12) interrupt
	}
 
}
 
//--------------------------------------------------------
// Used for passing commands or data to the PDIUSBD12
void D12_Write(unsigned char type, int data)
{
	int8 t;
	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
			t = 64;
			while(t--);				// 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, t;
 
	set_tris_d(0xFF);			// Set bus to intput mode
	for(i = 0; i<reads; i++)
	{
		output_low(D12_RD_N);
		t = 64;
		while(t--);				// Settling time (in PIC cycles)
		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 == 1){DEBUG7("\r\n");}	// DEBUG7 is a two-statement macro, so it need braces
		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 and many global variables (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 ");
			Handle_Ctrl_Out_EP();
		} else
		if (endpt_int & INT_CTRL_IN) {
			DEBUG2("CI ");
			Handle_Ctrl_In_EP();
		} else
		if (endpt_int & INT_ENDPT1_OUT){
			DEBUG3("1O ");
		}
		if (endpt_int & INT_ENDPT1_IN) {
			DEBUG3("1I ");
		} else
		if(endpt_int & INT_ENDPT2_OUT) {
			DEBUG3("2O ");
		} else
		if (endpt_int & INT_ENDPT2_IN) {
			DEBUG0("2I ");
			Send_Report(ENDPT2_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(){
	STATE = STATE_DEFAULT;		// Revert to default USB state
	CONFIGURATION = 0;			// Unconfigured
	ADDRESS = 0;				// Revert to default address
	SET_ADDRESS_PENDING = 0;	// Is not pending
	LOAD_CIN_OFFSET = 0;		// Start at beginning on next request
	LOAD_CIN_LENGTH = 0;		// Explicity state there is nothing to send
	CTRL_IN_BUSY = 0;			// It isn't
	CTRL_IN_SEND_REPORT = 0;	// Shouldn't be sending anything
	SEND_REPORT_OFFSET = 0;		// Ensure we start sending from the beginning
 
	D12_Write(D12_COMMAND, SET_ADDRESS);
	D12_Write(D12_DATA, 0x00 | 0x80);
 
	D12_Write(D12_COMMAND, SET_ENDPT_ENABLE);
	D12_Write(D12_DATA, 0x01);
 
	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
#SEPARATE
void 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);	// Clear interrupt
	D12_Read(buffer, 1);
	DEBUG3("LT=%x ", buffer[0]);
 
	if(TRUE/*buffer[0] & STAT_XFER_SUCCESS*/){			// HID-Class "Set_Report" never flags '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);
 
			// Cancel any current control endpoint transactions
			SEND_REPORT_OFFSET = 0;
			LOAD_COUT_LENGTH = 0;
			LOAD_CIN_LENGTH = 0;
			CTRL_IN_SEND_REPORT = FALSE;
 
 
			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
				if(DEBUG_LEVEL <= 1)Debug_Request(pReq);
 
				switch((pReq->bmRequestType & REQTYPE_CMD_TYPE) >> 5){
					// Standard request
					case 0x00:
						DEBUG4("SREQ ");
						Standard_Request(pReq);
					break;
 
					// Class request
					case 0x01:
						DEBUG4("CREQ ");
						Class_Request(pReq);
					break;
 
					// Endpoint request
					case 0x02:
						DEBUG4("EREQ ");
					break;
 
					// Unsupported
					default:
						DEBUG7("\x07");		// Bell character (^G)
						DEBUG7("?REQ=%x ", (pReq->bmRequestType & REQTYPE_CMD_TYPE) >> 5);
						// Stall this endpoint (indicating we cannot handle the request)
						Stall_Endpt(CTRL_OUT);
						Debug_Request(pReq);
					break;
				}
			} else {
				// Setup token is an invalid length
				Stall_Endpt(CTRL_OUT);
				Debug_Request(pReq);
			}
		} else
		if(LOAD_COUT_LENGTH - LOAD_COUT_OFFSET){	// Are we expecting data?
 
			// Smaller of "how much we're expecting" and "how much the buffer can hold".
			buffer[0] = ((LOAD_COUT_LENGTH - LOAD_COUT_OFFSET) > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : (LOAD_COUT_LENGTH - LOAD_COUT_OFFSET);
 
			D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_OUT);
			D12_Write(D12_COMMAND, READ_BUFFER);
			D12_Read(data, buffer[0]+2);	// Get reserved byte and D12 buffer length as well
 
			DEBUG4("EDl=%x ", buffer[0]);	// Expected Data length
			DEBUG4("RDl=%x ", data[1]);		// Received data length
 
			buffer[0] += 2;	// Avoid calculating "buffer[0]+2" each iter (we skip the reserved byte and D12 buffer length)
			for(i=2; i<buffer[0]; i++){
				REPORT_OUT[LOAD_COUT_OFFSET] = data[i];
				LOAD_COUT_OFFSET++;
			}
			D12_Write(D12_COMMAND, CLEAR_BUFFER);
 
			// Print recieved data to GBA if we're done
			if(LOAD_COUT_LENGTH - LOAD_COUT_OFFSET == 0){
				#use rs232(baud = 9600, xmit = PIN_C6, rcv = PIN_C7)	// Hardware USART (GBA)
				printf("[");
				for(i=0; i<LOAD_COUT_LENGTH; i++){
					printf("%x ", REPORT_OUT[i]);
				}
				printf("]");
				#use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5)	// Software USART (Debug)
			}
		} else {
			// Not a setup token and we're not expecting data
			DEBUG7("!Setup ");
			D12_Write(D12_COMMAND, SELECT_ENDPT + CTRL_OUT);
 
			D12_Write(D12_COMMAND, READ_BUFFER);
			D12_Read(data, D12_CTRL_BUFFER_SIZE + 2);	// Get preceeding reserved byte and D12 buffer length as well
 
			if(data[1] == 0){
				DEBUG2("N ");	// Null
			} else {
				data[1] = (data[1] < D12_CTRL_BUFFER_SIZE) ? data[1] : D12_CTRL_BUFFER_SIZE;
 
				data[1] += 2;	// Avoid calculating "data[1]+2" each iter (we skip the reserved byte and D12 buffer length)
				for(i=2; i<data[1]; i++){
					DEBUG7("%x ", data[i]);
				}
			}
		}
	} else
	if (buffer[0] & STAT_ERROR)		// Last transaction wasn't successful
	{
		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
#SEPARATE
void Stall_Endpt(int8 ENDPT)
{
	DEBUG7("S_");
	switch(ENDPT){
		case 0: DEBUG7("CO "); break;
		case 1: DEBUG7("CI "); break;
		case 2: DEBUG7("EO "); break;
		case 3: DEBUG7("EI "); break;
		case 4: DEBUG7("MO "); break;
		case 5: DEBUG7("MI "); break;
		default: DEBUG7("?(%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 Standard_Request(struct REQUEST *pReq) {
	short RequestOK = TRUE;
 
	switch(pReq->bRequest){
		case GET_STATUS_REQ:
			DEBUG4("Get_Staus ");
			if(STATE == STATE_CONFIGURED || (STATE == STATE_ADDRESSED && ADDRESS == 0)){
			} else {
				RequestOK = FALSE;
			}
		break;
 
		case CLEAR_FEATURE_REQ:
			DEBUG4("Clear_Feature ");
			if(STATE == STATE_CONFIGURED || (STATE == STATE_ADDRESSED && ADDRESS == 0)){
			} else {
				RequestOK = FALSE;
			}
		break;
 
		case SET_FEATURE_REQ:
			DEBUG4("Set_feature ");
			if(STATE == STATE_CONFIGURED || (STATE == STATE_ADDRESSED && ADDRESS == 0)){
			} else {
				RequestOK = FALSE;
			}
		break;
 
		case SET_ADDRESS_REQ:
			DEBUG4("Set_Address ");
			if(STATE == STATE_DEFAULT || STATE == STATE_ADDRESSED){
				Set_Address(pReq);
			} else {
				RequestOK = FALSE;
			}
		break;
 
		case GET_DESCRIPTOR_REQ:
			DEBUG4("Get_Descriptor ");
			if(STATE == STATE_DEFAULT || STATE == STATE_ADDRESSED || STATE == STATE_CONFIGURED){
				Get_Descriptor(pReq);
			} else {
				RequestOK = FALSE;
			}
		break;
 
		case SET_DESCRIPTOR_REQ:
			DEBUG4("Set_Descriptor ");
			if(STATE == STATE_ADDRESSED || STATE == STATE_CONFIGURED){
			} else {
				RequestOK = FALSE;
			}
		break;
 
		case GET_CONFIGURATION_REQ:
			DEBUG4("Get_Configuration ");
			if(STATE == STATE_ADDRESSED || STATE == STATE_CONFIGURED){	// Returns 0 if in addressed state
			} else {
				RequestOK = FALSE;
			}
		break;
 
		case SET_CONFIGURATION_REQ:
			DEBUG4("Set_Configuration ");
			if(STATE == STATE_ADDRESSED || STATE == STATE_CONFIGURED){
				Set_Configuration(pReq);
			} else {
				RequestOK = FALSE;
			}
		break;
 
		case GET_INTERFACE_REQ:
			DEBUG4("Get_Interface ");
			if(STATE == STATE_CONFIGURED){
			} else {
				RequestOK = FALSE;
			}
		break;
 
		case SET_INTERFACE_REQ:
			DEBUG4("Set_Interface ");
			if(STATE == STATE_CONFIGURED){
			} else {
				RequestOK = FALSE;
			}
		break;
 
		case SYNCH_FRAME_REQ:
			DEBUG4("Synch_Frame ");
			if(STATE == STATE_CONFIGURED){
			} else {
				RequestOK = FALSE;
			}
		break;
 
		default:
			RequestOK = FALSE;
			DEBUG7("\x07");		// Bell character (^G)
			DEBUG7("?SREQ=%x ", pReq->bRequest);
		break;
	}
 
	if(RequestOK == FALSE){
		DEBUG7("StateIncompatable(%x,%x) ", STATE, ADDRESS	);
		Stall_Endpt(CTRL_OUT);
		Debug_Request(pReq);
	}
}
 
//--------------------------------------------------------
// Service the host's request for a descriptor.
// Descriptos are stored end-to-end in a large block of ROM
// as the constant 'DESCRIPTORS'.
// This block of ROM is searched/parsed to see if it contains
// the requested descriptor.
void Get_Descriptor(struct REQUEST *pReq){
 
	unsigned int16 ReqDataLen, size;
	unsigned char i, type, stringCount;
	ReqDataLen = pReq->wLength;
	LOAD_CIN_LENGTH = 0;	// Ensure we don't send unrelated data
 
	DEBUG0("GD(%x) ", pReq->wValue >> 8);
	i = 0;
	stringCount = 0;
	while(i < sizeof(DESCRIPTORS)-1){
		// Read details from current descriptor
		size = (unsigned int16) DESCRIPTORS[i];
		type = DESCRIPTORS[i+1];
		DEBUG0("i=%x(%x, %x) ", i, size, type);
 
		if(type == (pReq->wValue >> 8)){	// High byte contains type
 
			// Take care of exceptions
			switch(type){
 
				// Configuration descriptors are expected to be sent with all their subordinate
				// descriptors.
 
				case 0x02:	// Configuration descriptor
					size =  (unsigned int16) DESCRIPTORS[i+2];		// wTotalLength low-byte
					size |= (unsigned int16) DESCRIPTORS[i+3] << 8;	// wTotalLength high-byte
					DEBUG0("*CD(%x) ", size);
				break;
 
				// There can be multiple string descriptors, addressed by index.
 
				case 0x03:	// String descriptor
					// Is there a need to try the next string?
					if( (pReq->wValue & 0x00FF) != stringCount){	// Low byte contains index for strings
						stringCount++;	// Note that we've checked a string
						i = i + size;	// Jump to next descriptor in loop
						continue;		// Restart while() loop
					}
				break;
 
 
				// Some descriptors do NOT contain bLength and bType bytes, these have been added
				// by the author to facilitate this parsing approach. Hence we reduce the size by 2
				// bytes and increase the LOAD_CIN_OFFSET by 2 bytes in such cases.
				// The artificial bLength value denotes the number of bytes until the next descriptor,
				// to ensure this code can 'step into' the next descriptor.
 
				case 0x22:	// Report descriptor
					i = i + 2;
					size = size - 2;
					DEBUG0("*RD(%x) ", size);
				break;
			}
 
			// Don't transmit more than we need to
			if( ReqDataLen > size) ReqDataLen = size;
 
			// Setup globals for the Ctrl_IN interrupt handler
			LOAD_CIN_OFFSET = i;	// Start of descriptor
			LOAD_CIN_LENGTH = ReqDataLen;
 
			DEBUG0("Lo=%x Ll=%x ", LOAD_CIN_OFFSET, LOAD_CIN_LENGTH);
 
			break; // Quit while() loop
		} else {
			// Types didn't match, try the next descriptor
			i = i + size;
		}
	}
 
	if(LOAD_CIN_LENGTH == 0){	// We didn't find a match
		DEBUG7("\x07");			// Bell character (^G)
		DEBUG7("?DR=%x ",(pReq->wValue >> 8));
		// Not sure which endpoint would need to be stalled..
		// presumably both as it's a bidirection endpoint of
		// sorts
		Stall_Endpt(CTRL_OUT);
		Stall_Endpt(CTRL_IN);
		Debug_Request(pReq);
	} else {
		Handle_Ctrl_In_EP();	// Kick-start descriptor sending
	}
}
 
//--------------------------------------------------------
// When an error code is encountered from a 'Read Last Transaction Command'
// this function is called to clean up the mess
 
#SEPARATE void Transaction_Error(int8 error){
	DEBUG7("!LT=%x ", error);
	switch (error)
	{
		case 0x02 : //0001 PID Encoding Error
		case 0x04 : //0010 PID Unknown
		case 0x06 : //0011 Unexpected packet
		case 0x08 : //0100 Token CRC Error
		case 0x0A : //0101 Data CRC Error
		case 0x0C : //0110 Time out Error
		case 0x0E : //0111 Never happens
		case 0x10 : //1000 Unexpected End of Packet
		case 0x12 : //1001 Sent or received NAK
		case 0x14 : //1010 Sent Stall, token received Endpt Stalled
		case 0x16 : //1011 Overflow Error
		case 0x1A : //1101 BitStuff Error
		case 0x1E : //1111 Wrong DATA PID
		break;
		default :
			DEBUG7("\x07");		// Bell character (^G)
			DEBUG7("?LT=%x ", error);
		break;
	}
}
 
//--------------------------------------------------------
// Output debugging info about a USB request
 
#SEPARATE void Debug_Request(struct REQUEST *pReq){
#ifdef __DEBUGGING_ENABLED
	printf("DIR=");
	if(pReq->bmRequestType & REQTYPE_XFER_DIRECTION){
		printf("I ");
	} else {
		printf("O ");
	}
 
	printf("TO=");
	switch(pReq->bmRequestType & REQTYPE_RECIPIENT){
 
		case 0x00: printf("D "); break;		// Device
		case 0x01: printf("I "); break;		// Interface
		case 0x02: printf("E "); break;		// Endpoint
		case 0x03: printf("? "); break;		// Other
 
		// Unsupported
		default:
			DEBUG7("\x07");		// Bell character (^G)
			// Stall this endpoint (indicating we cannot handle the request)
			Stall_Endpt(CTRL_OUT);
		break;
	}
 
	printf("wV=%Lx ", pReq->wValue);
	printf("wI=%Lx ", pReq->wIndex);
	printf("wL=%Lx ", pReq->wLength);
#endif
}
 
//--------------------------------------------------------
// Responds to the Set_Address request of the host, and then
// sets the D12 address (NB: The D12's address is actually
// changed AFTER the null packet phase )
void Set_Address(struct REQUEST *pReq){
 
	// The D12 buffers the Set_Address command and executes it upon recieving an ACK
	// in reply to the null packet we'll send in a moment.
	D12_Write(D12_COMMAND, SET_ADDRESS);
	D12_Write(D12_DATA, (pReq->wValue | 0x80));
 
	ADDRESS = (pReq->wValue | 0x80);			// Update our current address
	STATE = STATE_ADDRESSED;
 
	// Acknowledge token by replying with a null data packet
	Send_Null_Packet(CTRL_IN);
 
	CTRL_IN_BUSY = TRUE;	// Unless something else is pending, the next CTRL_IN interrupt just means the data was sent
	DEBUG3("AS ");
 
}
 
//--------------------------------------------------------
// 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 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);
 
	CTRL_IN_BUSY = TRUE;
 
	DEBUG3("Z ");
}
 
//--------------------------------------------------------
// Responds to Set_Confugration request of the host, checks the
// requested configuration is supported and responds appropriately with
// an ACK or a STALL, and updates the global state variable.
void Set_Configuration(struct REQUEST *pReq){
	short RequestOK = 1;
 
	switch(pReq->wValue & 0x00FF){	// Only interested in lower byte
		case 0:
			// Revert to unconfigured state
			STATE = STATE_ADDRESSED;
			CONFIGURATION = 0;
		break;
 
		case 1:
			STATE = STATE_CONFIGURED;
			CONFIGURATION = 1;
			Send_Report(ENDPT2_IN);		// Kick-start
		break;
 
		default:
			DEBUG7("!SetC=%x ", pReq->wValue & 0x0F);
			RequestOK = 0;
		break;
	}
 
	if(RequestOK){
		// Note that the CTRL_IN buffer was cleared in Handle_Ctrl_Out_EP();
		Send_Null_Packet(CTRL_IN);
	} else {
		// Indicate we don't like this request
		Stall_Endpt(CTRL_IN);
		Debug_Request(pReq);
	}
}
 
//--------------------------------------------------------
// Handle USB class requests
void Class_Request(struct REQUEST *pReq) {
	short RequestOK = TRUE;
 
	switch(pReq->bRequest){
 
		case SET_REPORT:
			DEBUG4("SR ");
			switch(pReq->wValue >> 8){	// High byte
				case	1:	DEBUG4("I ");	break;	// Input
				case	2:	DEBUG4("O ");	break;	// Output
				case	3:	DEBUG4("F ");	break;	// Feature
				default:	DEBUG7("? ");	break;
			}
			DEBUG4("RID=%x ", pReq->wValue & 0x00FF);	// Report ID (Low byte)
			DEBUG4("I=%Lx ", pReq->wIndex);
			DEBUG4("wL=%Lx ", pReq->wLength);
 
			// Setup globals to recieve data on CTRL_OUT
			LOAD_COUT_LENGTH = pReq->wLength;
			LOAD_COUT_OFFSET = 0;
 
			// Acknowledge
			Send_Null_Packet(CTRL_IN);
		break;
 
		case GET_REPORT:
			DEBUG4("GR ");
			switch(pReq->wValue >> 8){	// High byte
				case	1:	DEBUG4("I ");	break;	// Input
				case	2:	DEBUG4("O ");	break;	// Output
				case	3:	DEBUG4("F ");	break;	// Feature
				default:	DEBUG7("? ");	break;
			}
			DEBUG4("RID=%x ", pReq->wValue & 0x00FF);	// Report ID (Low byte)
			DEBUG4("I=%Lx ", pReq->wIndex);
			DEBUG4("wL=%Lx ", pReq->wLength);
 
			// Flag that we want to send a report
			CTRL_IN_SEND_REPORT = TRUE;
 
			// Acknowledge
			Send_Null_Packet(CTRL_IN);
		break;
 
		default:
			RequestOK = FALSE;
			DEBUG7("\x07");		// Bell character (^G)
			DEBUG7("?CREQ=%x ", pReq->bRequest);
		break;
	}
 
	if(RequestOK == FALSE){
		Stall_Endpt(CTRL_OUT);
		Debug_Request(pReq);
	}
}
 
//--------------------------------------------------------
// Responsible for sending data in response to setup tokens among other things.
// Activties include sending descriptors.
#SEPARATE
void Handle_Ctrl_In_EP() {
	unsigned char buffer[2];
 
	// Clear interrupt
	D12_Write(D12_COMMAND, READ_ENDPT_STATUS + CTRL_IN);
	D12_Read(buffer, 1);
	DEBUG1("LT=%x ", buffer[0]);
 
	if (LOAD_CIN_LENGTH > 0) {
		unsigned char DataLen, DataEnd;
 
		// Smaller of "length of data to send" and "buffer size"
		DataLen = (LOAD_CIN_LENGTH > D12_CTRL_BUFFER_SIZE) ? D12_CTRL_BUFFER_SIZE : LOAD_CIN_LENGTH;
		DataEnd = LOAD_CIN_OFFSET + DataLen;
		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
 
		for(; LOAD_CIN_OFFSET<DataEnd; LOAD_CIN_OFFSET++)
		{
			DEBUG0("%x ", DESCRIPTORS[LOAD_CIN_OFFSET]);
			D12_Write(D12_DATA, DESCRIPTORS[LOAD_CIN_OFFSET]);
			LOAD_CIN_LENGTH--;
		}
		D12_Write(D12_COMMAND, VALIDATE_BUFFER);	// Mark the buffer as ready to go!
 
		// Are we done?
		if(LOAD_CIN_LENGTH == 0){
			// Did the last packet fill the buffer completely?
			if((DataLen % D12_CTRL_BUFFER_SIZE) == 0){
				Send_Null_Packet(CTRL_IN);		// Explicity signal end of data
			}
		}
 
		CTRL_IN_BUSY = TRUE;	// Unless something else is pending, the next CTRL_IN interrupt just means the data was sent
 
		DEBUG2("DS ");
	}
	else
	if (CTRL_IN_SEND_REPORT)
	{
		// Send_Report returns true if there is not more data to send
		CTRL_IN_SEND_REPORT = ~(Send_Report(CTRL_IN));
	}
	else
	if (CTRL_IN_BUSY)
	{
		// This interrupt is just the D12 telling us it's emptied its buffer (i.e. sent it to the host)
		CTRL_IN_BUSY = FALSE;
		DEBUG0("ACK ");				// Actually D12 telling us the buffer was sent
	}
}
 
//--------------------------------------------------------
// Interrupt handler for USART hardware data reception
#INT_RDA
void Handle_UART_Reception() {
		char c;
		#use rs232(baud = 9600, xmit = PIN_C6, rcv = PIN_C7)	// Hardware USART (GBA)
		c = getc();
		#use rs232(baud = 19200, xmit = PIN_C4, rcv = PIN_C5)	// Software USART (Debug)
		DEBUG1("%c[%x] ", c, c);
		output_toggle(LED_N);
 
		switch(c){
			case '^': REPORT_IN.y += 10; break;
			case 'v': REPORT_IN.y -= 10; break;
			case '<': REPORT_IN.x += 10; break;
			case '>': REPORT_IN.x -= 10; break;
			case 'A': REPORT_IN.buttons ^= 0x01; break;	// Toggle bit
			case 'B': REPORT_IN.buttons ^= 0x02; break;	// Toggle bit
			case 's': REPORT_IN.buttons ^= 0x04; break;	// Toggle bit
			case 'L':
				REPORT_IN.x = rand();
				REPORT_IN.y = rand();
				REPORT_IN.buttons = rand();
			break;
			case 'S':
				REPORT_IN.x = 0;
				REPORT_IN.y = 0;
				REPORT_IN.buttons = 0;
			break;
			default:
				DEBUG1("%c[%x] ", c, c);
			break;
		}
}
 
//--------------------------------------------------------
// Fill an endpoint with report data.
// Should return false if more data is to follow, and should
// return true if all data has been sent.
//
// Note development of this function was never finished,
// the author was not successful in getting multi-part
// transfers working (i.e. report larger than the endpoint
// buffer).
//
// Also this function seems to return lots of null data
// when sending the report though CTRL_IN.
short Send_Report(int8 ENDPT) {
	int8 DataLen;
	DEBUG2("SndR ");
 
	switch(ENDPT){
		case CTRL_OUT:
		case CTRL_IN:
			DataLen = D12_CTRL_BUFFER_SIZE;
		break;
 
		case ENDPT2_OUT:
		case ENDPT2_IN:
			DataLen = D12_MAIN_BUFFER_SIZE;
		break;
 
		default:
			DEBUG7("!SR DataLen?");
			return TRUE;	// i.e. no more data to send
		break;
	}
 
	// Smaller of "data left to send" and "size of endpoint buffer"
	DataLen = ((sizeof(struct REPORT) - SEND_REPORT_OFFSET) > DataLen) ? DataLen : (sizeof(struct REPORT) - SEND_REPORT_OFFSET);
 
	DEBUG2("DL=%x ", DataLen);
 
	D12_Write(D12_COMMAND, READ_ENDPT_STATUS + ENDPT);	// Clear interrupt
	D12_Write(D12_COMMAND, SELECT_ENDPT + ENDPT);
	D12_Write(D12_COMMAND, CLEAR_BUFFER);				// (Probably unnecessary)
	D12_Write(D12_COMMAND, WRITE_BUFFER);
	D12_Write(D12_DATA, 0x00);							// Reserved byte
	D12_Write(D12_DATA, DataLen);
 
	DataLen += SEND_REPORT_OFFSET;
	for(; SEND_REPORT_OFFSET < DataLen; SEND_REPORT_OFFSET++){
		D12_Write(D12_DATA, *(&REPORT_IN + SEND_REPORT_OFFSET));
		DEBUG2("%x ",*(&REPORT_IN + SEND_REPORT_OFFSET));
	}
	D12_Write(D12_COMMAND, VALIDATE_BUFFER);			// Fit to be sent
 
	// Are we done?
	if(SEND_REPORT_OFFSET == sizeof(struct REPORT)){
		SEND_REPORT_OFFSET = 0;
		return TRUE;
	} else {
		return FALSE;
	}
 
}
project/usb/code/joystick.txt · Last modified: 2007/04/27 00:21 (external edit)