USB Code for the MZ

Here is some minimal USB code for the MZ processor written specifically for the hobbyist or developer that does not want to, or just can't, invest in the time required to figure out how to use Harmony. My code uses Microsoft OS Descriptors to load a Winusb driver. The only requirement is a Windows or Linux based application that can interface with Winusb.

The Code

Main.c
  1 /*********************************************************************
  2     FileName:           Main.c
  3     Dependencies:       See #includes
  4     Processor:          PIC32MZ
  5     Hardware:           MainBrain32 rev 0.20
  6     Complier:       XC32 4.40
  7     Author:         Larry Knight 2023
  8 
  9     Software License Agreement:
 10         This software is licensed under the Apache License Agreement
 11 
 12     Description:
 13 
 14     File Description:
 15 
 16     Change History:
 17 /***********************************************************************/
 18 
 19 #pragma config DEBUG =      OFF
 20 #pragma config JTAGEN =     OFF
 21 #pragma config ICESEL =     ICS_PGx2
 22 #pragma config TRCEN =      OFF
 23 #pragma config BOOTISA =    MIPS32
 24 #pragma config FECCCON =    OFF_UNLOCKED
 25 #pragma config FSLEEP =     OFF
 26 #pragma config DBGPER =     PG_ALL
 27 #pragma config SMCLR =      MCLR_NORM
 28 #pragma config SOSCGAIN =   GAIN_2X
 29 #pragma config SOSCBOOST =  ON
 30 #pragma config POSCGAIN =   GAIN_2X
 31 #pragma config POSCBOOST =  OFF
 32 #pragma config EJTAGBEN =   NORMAL
 33 #pragma config CP =         OFF
 34 
 35 /*** DEVCFG1 ***/
 36 #pragma config FNOSC =      SPLL
 37 #pragma config DMTINTV =    WIN_127_128
 38 #pragma config FSOSCEN =    OFF
 39 #pragma config IESO =       OFF
 40 #pragma config POSCMOD =    EC
 41 #pragma config OSCIOFNC =   ON
 42 #pragma config FCKSM =      CSECME
 43 #pragma config WDTPS =      PS1048576
 44 #pragma config WDTSPGM =    STOP
 45 #pragma config FWDTEN =     OFF
 46 #pragma config WINDIS =     NORMAL
 47 #pragma config FWDTWINSZ =  WINSZ_25
 48 #pragma config DMTCNT =     DMT31
 49 #pragma config FDMTEN =     OFF
 50 
 51 /*** DEVCFG2 ***/
 52 //250 MHz Core
 53 #pragma config FPLLIDIV =   DIV_3
 54 #pragma config FPLLRNG =    RANGE_5_10_MHZ
 55 #pragma config FPLLICLK =   PLL_POSC
 56 #pragma config FPLLMULT =   MUL_60
 57 #pragma config FPLLODIV =   DIV_2
 58 #pragma config UPLLFSEL =   FREQ_24MHZ
 59 
 60 /*** DEVCFG3 ***/
 61 #pragma config USERID =     0xffff
 62 #pragma config FMIIEN =     ON
 63 #pragma config FETHIO =     ON
 64 #pragma config PGL1WAY =    OFF
 65 #pragma config PMDL1WAY =   OFF
 66 #pragma config IOL1WAY =    OFF
 67 #pragma config FUSBIDIO =   ON
 68 
 69 #include <xc.h>
 70 #include <p32xxxx.h>
 71 #include <proc/p32mz0512efk100.h>
 72 #include <sys/attribs.h>
 73 #include <sys/kmem.h>
 74 #include <stdint.h> 
 75 #include <stdio.h>
 76 #include <stdlib.h>
 77 #include <string.h>
 78 #include <stdbool.h>
 79 
 80 void USB_init(void);
 81 void SystemSetup(void);
 82 void LED_Port(unsigned led_port_data);
 83 
 84 int main(void)
 85 {       
 86     while(CLKSTATbits.POSCRDY == 0);
 87         
 88     SystemSetup();
 89     
 90     LED_Port(0x0);
 91     
 92     USB_init();
 93     
 94     while(1)
 95     {
 96         //Heart beat
 97         PORTAbits.RA5 = !PORTAbits.RA5;
 98     }
 99 }
100 
101 //Timer 2
102 void __attribute__((vector(_TIMER_2_VECTOR), interrupt(ipl3srs), nomips16)) timer2_handler()
103 {
104     // Reset interrupt flag
105     IFS0bits.T2IF = 0;
106 }
107 
108 void SystemSetup(void)
109 {   
110         unsigned int cp0;
111     
112     ANSELA = 0;
113     ANSELD = 0;
114     ANSELF = 0;
115     ANSELG = 0;
116     
117     TRISAbits.TRISA5 = 0;
118     TRISGbits.TRISG12 = 0;
119     TRISGbits.TRISG13 = 0;
120     TRISGbits.TRISG14 = 0;
121     TRISAbits.TRISA7 = 0;
122     TRISAbits.TRISA6 = 0;
123     TRISDbits.TRISD1 = 0;
124     TRISDbits.TRISD4 = 0;
125     TRISDbits.TRISD5 = 0;
126     
127     TRISF = 1;
128     
129     /* Enable pull-up resistor(s) */
130     CNPUFbits.CNPUF3 = 1;       // Pull-up enables on RPF3 (To set USB as a B-Device )
131         
132     PRISS = 0x76543210; 
133     
134     __builtin_disable_interrupts(); // Disable all interrupt
135     
136     // Unlock Sequence
137     SYSKEY = 0xAA996655;
138     SYSKEY = 0x556699AA;  
139 
140     // PB1DIV
141     // Peripheral Bus 1 cannot be turned off, so there's no need to turn it on
142     PB1DIVbits.PBDIV = 1; // Peripheral Bus 1 Clock Divisor Control (PBCLK1 is SYSCLK divided by 2)
143 
144     // PB2DIV
145     PB2DIVbits.ON = 1; // Peripheral Bus 2 Output Clock Enable (Output clock is enabled)
146     PB2DIVbits.PBDIV = 1; // Peripheral Bus 2 Clock Divisor Control (PBCLK2 is SYSCLK divided by 2)
147 
148     // PB3DIV
149     PB3DIVbits.ON = 1; // Peripheral Bus 2 Output Clock Enable (Output clock is enabled)
150     PB3DIVbits.PBDIV = 1; // Peripheral Bus 3 Clock Divisor Control (PBCLK3 is SYSCLK divided by 2)
151 
152     // PB4DIV
153     PB4DIVbits.ON = 1; // Peripheral Bus 4 Output Clock Enable (Output clock is enabled)
154     while (!PB4DIVbits.PBDIVRDY); // Wait until it is ready to write to
155     PB4DIVbits.PBDIV = 0; // Peripheral Bus 4 Clock Divisor Control (PBCLK4 is SYSCLK divided by 1)
156 
157     // PB5DIV
158     PB5DIVbits.ON = 1; // Peripheral Bus 5 Output Clock Enable (Output clock is enabled)
159     PB5DIVbits.PBDIV = 1; // Peripheral Bus 5 Clock Divisor Control (PBCLK5 is SYSCLK divided by 2)
160 
161     // PB7DIV
162     PB7DIVbits.ON = 1; // Peripheral Bus 7 Output Clock Enable (Output clock is enabled)
163     PB7DIVbits.PBDIV = 0; // Peripheral Bus 7 Clock Divisor Control (PBCLK7 is SYSCLK divided by 1)
164 
165     // PB8DIV
166     PB8DIVbits.ON = 1; // Peripheral Bus 8 Output Clock Enable (Output clock is enabled)
167     PB8DIVbits.PBDIV = 1; // Peripheral Bus 8 Clock Divisor Control (PBCLK8 is SYSCLK divided by 2)
168 
169     // PRECON - Set up prefetch
170     PRECONbits.PFMSECEN = 0; // Flash SEC Interrupt Enable (Do not generate an interrupt when the PFMSEC bit is set)
171     PRECONbits.PREFEN = 0b11; // Predictive Prefetch Enable (Enable predictive prefetch for any address)
172     PRECONbits.PFMWS = 0b010; // PFM Access Time Defined in Terms of SYSCLK Wait States (Two wait states)
173     
174     CFGCONbits.USBSSEN = 1;
175 
176     // Set up caching
177     cp0 = _mfc0(16, 0);
178     cp0 &= ~0x07;
179     cp0 |= 0b011; // K0 = Cacheable, non-coherent, write-back, write allocate
180     _mtc0(16, 0, cp0);  
181 
182     INTCONbits.MVEC = 1; // Global interrupt enable
183 
184     // Lock 
185     SYSKEY = 0x33333333;
186     
187     __builtin_enable_interrupts();    
188 }
189 
190 void LED_Port(unsigned led_port_data)
191 {
192     //LED0              PORTDbits.RD1
193     //LED1              PORTDbits.RD4 
194     //LED2              PORTDbits.RD5
195     //LED3              PORTAbits.RA6
196     //LED4              PORTAbits.RA7
197     //LED5              PORTGbits.RG13
198     //LED6              PORTGbits.RG14
199     //LED7              PORTGbits.RG12
200 
201     uint32_t temp = led_port_data;
202     
203     //LED0
204     temp = led_port_data & 0x01;  
205     PORTDbits.RD1 = temp;
206     
207     //LED_1
208     temp = led_port_data & 0x02;
209     temp = temp >> 1;
210     PORTDbits.RD4 = temp;
211     
212     //LED_2
213     temp = led_port_data & 0x04;
214     temp = temp >> 2;
215     PORTDbits.RD5 = temp;
216     
217     //LED_3
218     temp = led_port_data & 0x08;
219     temp = temp >> 3;
220     PORTAbits.RA6 = temp;
221     
222     //LED_4
223     temp = led_port_data & 0x10;
224     temp = temp >> 4;
225     PORTAbits.RA7 = temp;
226     
227     //LED_5
228     temp = led_port_data & 0x20;
229     temp = temp >> 5;
230     PORTGbits.RG13 = temp;
231     
232     //LED_6
233     temp = led_port_data & 0x40;
234     temp = temp >> 6;
235     PORTGbits.RG14 = temp;
236     
237     //LED_7
238     temp = led_port_data & 0x80;
239     temp = temp >> 7;
240     PORTGbits.RG12 = temp;
241        
242 }
243 
244 
245 
USB_MZ.c
  1 /*********************************************************************
  2     FileName:           USB_MZ.c
  3     Dependencies:       See #includes
  4     Processor:          PIC32MZ
  5     Hardware:           MainBrain32 rev 0.20
  6     Complier:       XC32 4.40
  7     Author:         Larry Knight 2023
  8 
  9     Software License Agreement:
 10         This software is licensed under the Apache License Agreement
 11 
 12     Description:
 13         Enumerates as a High Speed interface class device,
 14         Uses Microsoft OS Descriptors to load a Winusb driver,
 15         Endpoint 1 is the receiving endpoint,
 16         Endpoint 2 is the transmitting endpoint,
 17         Host application sends commands to the device, 
 18         Device responds to the commands by sending requested data to the Host
 19 
 20     Device Interface GUID:
 21     2b8a8216-c82a-4a91-a8bc-a12129d2d70b
 22 
 23     References:        
 24         https://techcommunity.microsoft.com/t5/microsoft-usb-blog/how-does-usb-stack-enumerate-a-device/ba-p/270685#_Configuration_Descriptor_Request
 25 
 26     Registry Stuff:
 27         HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags\120900010200
 28         HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_1209&PID_0001\MainBrain_MZ
 29 
 30     Change History:
 31         Basic framework completed 06/16/2024
 32 
 33 /***********************************************************************/
 34 
 35 #include <xc.h>
 36 #include <p32xxxx.h>
 37 #include <proc/p32mz0512efk100.h>
 38 #include <sys/attribs.h>
 39 #include <sys/kmem.h>
 40 #include <stdint.h> 
 41 #include <stdio.h>
 42 #include <stdlib.h>
 43 #include <string.h>
 44 #include <stdbool.h>
 45 
 46 enum USB_State
 47 {
 48     POWERED,
 49     DEFAULT,
 50     ADDRESS,
 51     CONFIGURED,
 52     ATTACHED,
 53     SUSPEND
 54 };
 55 
 56 enum USB_State DeviceState;
 57 
 58 typedef struct
 59 {
 60     volatile unsigned char bmRequestType;
 61     volatile unsigned char bRequest;
 62     volatile unsigned short wValue;
 63     volatile unsigned short wIndex;
 64     volatile unsigned short wLength;
 65 } USB_TRANSACTION;
 66 
 67 USB_TRANSACTION USB_transaction;
 68 
 69 typedef struct
 70 {
 71     volatile unsigned short rx_num_bytes;
 72     volatile unsigned short tx_num_bytes;
 73     volatile unsigned char tx_buffer[512];
 74     volatile unsigned char rx_buffer[512];
 75 } USB_ENDPOINT;
 76 
 77 USB_ENDPOINT EP[3];
 78 
 79 uint8_t device_descriptor[] = 
 80 {
 81     /* Descriptor Length                                                */ 0x12, //Size of this descriptor in bytes
 82     /* DescriptorType: DEVICE                                   */ 0x01,
 83     /* bcdUSB (ver 2.0)                                                 */ 0x00,0x02,
 84     /* bDeviceClass                                                             */ 0x00,
 85     /* bDeviceSubClass                                                  */ 0x00,
 86     /* bDeviceProtocol                                                  */ 0x00,
 87     /* bMaxPacketSize0                                                  */ 0x40, //0x40 for High Speed USB
 88     /* idVendor                                                                 */ 0x09,0x12, /*VID */
 89     /* idProduct                                                                */ 0x01,0x00, 
 90     /* bcdDevice                                                                */ 0x00,0x02, 
 91     /* iManufacturer                                                    */ 0x01,
 92     /* iProduct                                                                 */ 0x02,
 93     /* iSerialNumber                                                    */ 0x02, 
 94     /* bNumConfigurations                                               */ 0x01
 95 };
 96 
 97 uint8_t config_descriptor[] = 
 98 {
 99     // Configuration Descriptor
100     0x09,                       //Descriptor size in bytes
101     0x02,                       //Descriptor type
102     0x20,0x00,                  //Total length of data
103     0x01,                       //Number of interfaces
104     0x01,                       //Index value of this configuration
105     0x00,                       //Configuration string index
106     0xc0,                       // Attributes, see usb_device.h
107     0x32,                       // Max power consumption (2X mA)
108                                                         
109     // Interface Descriptor
110     0x09,                       // Size of this descriptor in bytes
111     0x04,                       // INTERFACE descriptor type
112     0x00,                       // Interface Number
113     0x00,                       // Alternate Setting Number
114     0x02,                       // Number of endpoints in this intf
115     0xff,                       // Class code
116     0xff,                       // Subclass code
117     0xff,                       // Protocol code
118     0x00,                       // Interface string index
119     
120     // Endpoint Descriptor
121     //EP01 OUT
122     0x07,                       //Size of this descriptor in bytes
123     0x05,                       //Endpoint Descriptor
124     0x01,                       //EndpointAddress
125     0x02,                       //Attributes
126     0x40,0x00,                  //size
127     0x01,                       //Interval   
128     //EP02 IN                      
129     0x07,                       //Size of this descriptor in bytes
130     0x05,                       //Endpoint Descriptor
131     0x82,                       //EndpointAddress
132     0x02,                       //Attributes
133     0x40,0x00,                  //size
134     0x01                        //Interval
135 };
136 
137 uint8_t device_qualifier[] = 
138 {
139     0x0a,                       //Size of this descriptor in bytes
140     0x06,                       //Descriptor type (0x06)
141     0x00, 0x02,                 //BCD - USB version number (Must be 0x200 or higher)
142     0xff,                       //Class Code
143     0xff,                       //Subclass Code
144     0xff,                       //Protocol
145     0x40,                       //bMaxPacketSize0
146     0x01,                       //bNumConfigurations
147     0x00                        //Reserved
148 
149 };
150 
151 uint8_t MSOSDescriptor[] =
152 {   
153     //bLength - length of this descriptor in bytes
154     0x0b,                           
155     //bDescriptorType - "string"
156     0x03,                           
157     //qwSignature - special values that specifies the OS descriptor spec version that this firmware implements
158     'M','S','F','T','1','0','0',    
159     //bMS_VendorCode - defines the "GET_MS_DESCRIPTOR" bRequest literal value
160     0xee,
161     //bFlags
162     //a new flags field has been added to the Microsoft OS string descriptor that can be used to indicate support for the ContainerID descriptor
163     //Bit 1 of this field is used to indicate support for the ContainerID descriptor
164     0x00                            
165 };    
166 
167 //Extended Compatability ID Feature Descriptor
168 uint8_t ExtCompatIDFeatureDescriptor[] =
169 {
170     0x28, 0x00, 0x00, 0x00,                             /* dwLength Length of this descriptor */
171     0x00, 0x01,                                         /* bcdVersion = Version 1.0 */
172     0x04, 0x00,                                         /* wIndex = 0x0004 */
173     0x01,                                               /* bCount = 1 */
174     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,           /* Reserved */
175     0x00,                                               /* Interface number = 0 */
176     0x01,                                               /* Reserved */
177     0x57, 0x49, 0x4E, 0x55, 0x53, 0x42, 0x00, 0x00,     /* compatibleID */ //WINUSB
178     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,     /* subCompatibleID */
179     0x00, 0x00, 0x00, 0x00, 0x00, 0x00                  /* Reserved */
180 };
181     
182 uint8_t ExtPropertyFeatureDescriptor[] =
183 {
184     //----------Header Section--------------
185     0x8e, 0x00, 0x00, 0x00,                 //dwLength (4 bytes)
186     0x00, 0x01,                             //bcdVersion = 1.00
187     0x05, 0x00,                             //wIndex
188     0x01, 0x00,                             //wCount - 0x0001 "Property Sections" implemented in this descriptor
189     //----------Property Section 1----------
190     0x84, 0x00, 0x00, 0x00,                 //dwSize - 132 bytes in this Property Section
191     0x01,0x00, 0x00, 0x00,                  //dwPropertyDataType (Unicode string)
192     0x28, 0x00,                                  //wPropertyNameLength - 40 bytes in the bPropertyName field
193     'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0,
194         'e', 0, 'G', 0, 'U', 0, 'I', 0, 'D', 0,  0x00, 0x00,  //bPropertyName - "DeviceInterfaceGUID"
195     0x4e, 0x00, 0x00, 0x00,                  //dwPropertyDataLength - 78 bytes in the bPropertyData field (GUID value in UNICODE formatted string, with braces and dashes)
196     //Device Interface GUID
197     //{2b8a8216-c82a-4a91-a8bc-a12129d2d70b}
198     '{', 0, '2', 0, 'b', 0, '8', 0, 'a', 0, '8', 0, '2', 0, '1', 0, '6', 0, '-', 0, 'c', 0, '8', 0, '2', 0, 'a', 0, 
199         '-', 0, '4', 0, 'a', 0, '9', 0, '1', 0, '-', 0, 'a', 0, '8', 0, 'b', 0, 'c', 0, '-', 0, 'a', 0, '1', 0, '2', 
200         0, '1', 0, '2', 0, '9', 0, 'd', 0, '2', 0, 'd', 0, '7', 0, '0', 0, 'b', 0, '}', 0, 0x00, 0x00
201 };    
202 
203 //Language - 0x0409 - English
204 uint8_t string0[] =  {4, 0x03, 0x09, 0x04};
205 
206 //iManufacturer
207 uint8_t string1[] = {26, 3, 'L', 0, 'a', 0, 'r', 0, 'r', 0, 'y', 0, ' ', 0, 'K', 0, 'n', 0, 'i', 0, 'g', 0, 'h', 0, 't', 0};   
208 
209 //iProduct      
210 uint8_t string2[] = {26, 3, 'M', 0, 'a', 0, 'i', 0, 'n', 0, 'B', 0, 'r', 0, 'a', 0, 'i', 0, 'n', 0, ' ', 0, 'M', 0, 'Z', 0};
211  
212 //iSerialNumber 
213 uint8_t string3[] = {10, 3, '0', 0, '0', 0, '0', 0, '1', 0};
214 
215 void EP0_control_transaction(void);
216 void USB_queue_EP0(uint8_t *buffer, int size, int max_size);
217 void EP0_RX(int length);
218 void EP0_TX(void);
219 void Host_CMDs(void);
220 int EP1_RX(void);
221 int EP2_TX(volatile uint8_t *tx_buffer);
222 int EP0_Wait_TXRDY(void);
223 int EP2_Wait_TXRDY(void);
224 
225 volatile uint8_t usbAddress;
226 volatile bool SetAddress = true;
227 
228 void USB_init(void)
229 {
230     //disable while module is setup
231     USBCSR0bits.SOFTCONN = 0;   
232     
233     //EP 1
234     //These bits select which endpoint registers are accessed through addresses 0x3010-0x301F
235     USBCSR3bits.ENDPOINT = 1;
236 
237     //RX
238     USBOTGbits.RXFIFOSZ = 0x06;
239     USBIENCSR1bits.RXMAXP = 64;
240     USBIENCSR3bits.RXFIFOSZ = 0x09;
241     USBFIFOAbits.RXFIFOAD = 0x0280;
242     USBIENCSR3bits.PROTOCOL = 0x02;
243     USBIENCSR3bits.TEP = 0x01;
244     USBIENCSR1bits.FLUSH = 1;
245     
246     
247     //TX
248     USBOTGbits.TXFIFOSZ = 0x06;
249     USBIENCSR0bits.TXMAXP = 64;
250     USBIENCSR3bits.TXFIFOSZ = 0x09;
251     USBFIFOAbits.TXFIFOAD = 0x0080;
252     USBIENCSR2bits.PROTOCOL = 0x02;
253     USBIENCSR2bits.TEP = 0x02;
254     USBIENCSR0bits.FLUSH = 1;
255 
256     //Endpoint 1 is RX
257     USBE1CSR0bits.MODE = 0;
258     
259     //Endpoint 2 is TX
260     USBE2CSR0bits.MODE = 1;
261     
262     // Set endpoint 0 buffer to 64 bytes (multiples of 8).
263     USBE0CSR0bits.TXMAXP = 64; 
264 
265     //Clear the address
266     usbAddress = 0;                
267     USBCSR0bits.FUNC = 0;   
268     
269     // Enable the reset interrupt
270     USBCSR2bits.RESETIE = 1;    
271     
272     // Enable the USB interrupt
273     IEC4bits.USBIE = 1;       
274     
275     // Enable USB module interrupt
276     USBCRCONbits.USBIE = 1;     
277     
278     // Clear the USB interrupt flag.
279     IFS4bits.USBIF = 0;         
280     
281     // USB Interrupt Priority 7
282     IPC33bits.USBIP = 7;        
283     
284     // USB Interrupt Sub-Priority 1
285     IPC33bits.USBIS = 1;   
286     
287     // See DISNYET (same bit as PIDERR)
288     USBE1CSR1bits.PIDERR = 1;   
289 
290     // Enable High Speed (480Mbps) USB mode
291     USBCSR0bits.HSEN = 1;       
292 
293     USBCSR0bits.SOFTCONN = 1;
294     
295     DeviceState = ATTACHED;
296 }
297 
298 //USB
299 void __attribute__((vector(_USB_VECTOR), interrupt(ipl7srs), nomips16)) USB_handler()
300 {   
301     //Reset
302     if(USBCSR2bits.RESETIF)
303     {
304         // 1 = Endpoint is TX
305         USBE1CSR0bits.MODE = 1;     
306         
307         // Set endpoint 0 buffer to 64 bytes (multiples of 8)
308         USBE0CSR0bits.TXMAXP = 64; 
309         
310         // Endpoint 0 Operating Speed Control bits
311         USBE0CSR2bits.SPEED = 1;
312         
313         // Endpoint 1: TX Endpoint Operating Speed Control bits - High speed        
314         USBE1CSR2bits.SPEED = 1;        
315         
316         // Endpoint 1 - Maximum TX Payload Per Transaction Control bits
317         USBE1CSR0bits.TXMAXP = 64;
318         
319         // Endpoint 1 - TX Endpoint Protocol Control bits 
320         USBE1CSR2bits.PROTOCOL = 2; 
321 
322         //PROTOCOL<1:0>: RX/TX Endpoint Protocol Control bits 
323         //11 = Interrupt
324         //10 = Bulk
325         //01 = Isochronous
326         //00 = Control
327         
328         USBCSR1bits.EP1TXIE = 1;    // Endpoint 1 TX interrupt enable
329         USBCSR2bits.EP1RXIE = 1;    // Endpoint 1 RX interrupt enable
330         
331         USBCSR2bits.RESETIF = 0;
332     }
333     
334     /* Endpoint 0 Interrupt Handler */
335     if(USBCSR0bits.EP0IF == 1)
336     { 
337         // Do we need the set the USB address?
338         if (SetAddress == true)
339         {
340             //This sets a limit of 127
341             USBCSR0bits.FUNC = usbAddress & 0x7F;
342             SetAddress = false;
343         }
344         
345         if(USBE0CSR0bits.RXRDY)
346         {
347             EP0_RX(USBE0CSR2bits.RXCNT);
348             
349             USB_transaction.bmRequestType = EP[0].rx_buffer[0];
350             USB_transaction.bRequest = EP[0].rx_buffer[1];
351             USB_transaction.wValue = (int)(EP[0].rx_buffer[3] << 8) | EP[0].rx_buffer[2];
352             USB_transaction.wIndex = (int)(EP[0].rx_buffer[5] << 8) | EP[0].rx_buffer[4];
353             USB_transaction.wLength = (int)(EP[0].rx_buffer[7] << 8) | EP[0].rx_buffer[6];
354             
355             EP0_control_transaction();
356             
357             // End of Data Control bit (Device mode) 
358             if (USB_transaction.wLength == 0)
359             {
360                 USBE0CSR0bits.DATAEND = 1; 
361             }
362         }
363                 
364         if (USBE0CSR0bits.SETEND) 
365         {
366             USBE0CSR0bits.SETENDC = 1;
367         }
368         
369         // Clear the USB EndPoint 0 Interrupt Flag.
370         USBCSR0bits.EP0IF = 0;  
371     }
372     
373     //Endpoint 1 Interrupt Handler
374     if(USBCSR1bits.EP1RXIF == 1)
375     { 
376         EP1_RX();
377         Host_CMDs();
378         USBCSR1bits.EP1RXIF = 0;
379     }
380 
381     IFS4bits.USBIF = 0;   
382 }
383 
384 void Host_CMDs()
385 {
386   switch (EP[1].rx_buffer[0])
387   {
388       case 0x00:
389           EP[2].tx_buffer[0] = 34;
390           EP2_TX(EP[2].tx_buffer);
391         break;
392       case 0x01:
393           EP[2].tx_buffer[0] = 87;
394           EP2_TX(EP[2].tx_buffer);
395         break;
396   }
397 }
398 
399 int EP2_TX(volatile uint8_t* tx_buffer)
400 {
401     int cnt = 0;
402 
403     //Load the data to TX in array
404     EP[2].tx_num_bytes = 64;
405     
406     for (cnt = 0; cnt < 64; cnt++)
407     {
408         EP[2].tx_buffer[cnt] = tx_buffer[cnt];
409     }       
410         
411     //a pointer
412     uint8_t *FIFO_buffer;
413 
414     //load the pointer with the address of the TX buffer
415     FIFO_buffer = (uint8_t *)&USBFIFO2;
416     
417     //return if the TX buffer is empty
418     if (EP2_Wait_TXRDY())
419     {
420         return 0;
421     }
422     
423     //reset cnt
424     cnt = 0;
425     
426     //send data until the TX buffer is empty
427     while (cnt < EP[2].tx_num_bytes)
428     {
429         *FIFO_buffer = EP[2].tx_buffer[cnt]; // Send the bytes
430 
431         cnt++;
432         
433         // Have we sent 64 bytes?
434         if ((cnt > 0) && (cnt % 64 == 0))
435         {
436             //Set TXRDY and wait for it to be cleared before sending any more bytes
437             USBE2CSR0bits.TXPKTRDY = 1;            
438             if(EP2_Wait_TXRDY())
439             {
440                 return 0;
441             }            
442         }
443     }
444 
445     USBE2CSR0bits.TXPKTRDY = 1;            
446     
447 }
448 int EP1_RX()
449 {
450     unsigned char *FIFO_buffer;
451     int cnt;
452     int rx_bytes;
453     
454     //get the number of bytes received
455     rx_bytes = USBE1CSR2bits.RXCNT;
456     
457     //USB FIFO Data Register 1
458     FIFO_buffer = (unsigned char *)&USBFIFO1; 
459     
460     //load the array with the bytes in the buffer
461     for(cnt = 0; cnt < rx_bytes; cnt++)
462     {
463         EP[1].rx_buffer[cnt] = *(FIFO_buffer + (cnt & 3));
464     }
465     
466     //unload the RX FIFO
467     USBE1CSR1bits.RXPKTRDY = 0;
468 
469     return rx_bytes;
470 }
471 
472 
473 void EP0_control_transaction()
474 {
475     uint16_t length;
476 
477     if ((USB_transaction.bmRequestType == 0xC0) && (USB_transaction.wIndex == 0x04))
478     {
479        length = USB_transaction.wLength;
480        if (length > sizeof(ExtCompatIDFeatureDescriptor))
481        {
482            length = sizeof(ExtCompatIDFeatureDescriptor);
483        }
484        
485        USB_queue_EP0(ExtCompatIDFeatureDescriptor, sizeof(ExtCompatIDFeatureDescriptor), length); 
486        
487        return;
488     }
489     
490     //Class specific, device to host, interface target
491     if(USB_transaction.bmRequestType == 0xc1)    
492     {
493         //Check if the host is requesting an MS feature descriptor
494         if(USB_transaction.bRequest == 0xee)
495         {
496             //Figure out which descriptor is being requested
497             if(USB_transaction.wIndex == 0x05)    
498             {
499                 //Determine number of bytes to send to host 
500                 //Lesser of: requested amount, or total size of the descriptor
501                 length = sizeof(ExtPropertyFeatureDescriptor);
502                 if(USB_transaction.wLength < length)
503                 {
504                     length = USB_transaction.wLength;
505                 }
506                 
507                 USB_queue_EP0(ExtPropertyFeatureDescriptor, sizeof(ExtPropertyFeatureDescriptor), length);  
508                         
509                 USBE0CSR0bits.TXRDY = 1;    
510                 
511                 return;
512             }
513         }
514     }
515     
516     // We're not going to bother with whether bmRequestType is IN or OUT for the most part
517     switch (USB_transaction.bRequest)
518     {
519         case 0xC:
520         {
521             USBE0CSR0bits.STALL = 1;
522             break;
523             
524         }
525         case 0x0: 
526         {
527             if (USB_transaction.bmRequestType == 0x80) // Get status
528                 USB_queue_EP0(device_descriptor, 0, 0);
529             if (USB_transaction.bmRequestType == 0x00) // Select function
530                 USB_queue_EP0(device_descriptor, 0, 0);
531             break;            
532         }
533         
534         //Set USB address
535         case 0x5: 
536         {
537             USBE0CSR0bits.RXRDYC = 1;
538             usbAddress = EP[0].rx_buffer[2];
539 
540             SetAddress = true;
541             break;
542         }
543         
544         //Get descriptor
545         case 0x6: 
546         {
547             switch (USB_transaction.wValue >> 8)
548             {
549                 //Device descriptor
550                 case 0x1: 
551                 {
552                     USB_queue_EP0(device_descriptor, sizeof(device_descriptor), USB_transaction.wLength);                             
553                     break;
554                 }
555                 
556                 //Configuration descriptor
557                 case 0x2: 
558                 {
559                     USB_queue_EP0(config_descriptor, sizeof(config_descriptor), USB_transaction.wLength);
560                     break;
561                 }
562                 
563                 //String descriptors
564                 case 0x3: 
565                 {          
566                     switch (USB_transaction.wValue & 0xff)
567                     {
568                         //String 0 - Language ID
569                         case 0x0: 
570                         {
571                             USB_queue_EP0(string0, sizeof(string0), USB_transaction.wLength);
572                             break;
573                         }
574                         //String 1 - iManufacturer
575                         case 0x1: 
576                         {
577                             USB_queue_EP0(string1, sizeof(string1), USB_transaction.wLength);                           
578                             break;
579                         }
580                         //String 2 - iProduct
581                         case 0x2: 
582                         {
583                             USB_queue_EP0(string2, sizeof(string2), USB_transaction.wLength);
584                             break;
585                         }
586                         //String 3 - iSerialNumber
587                         case 0x3: 
588                         {
589                             USB_queue_EP0(string3, sizeof(string3), USB_transaction.wLength);
590                             break;
591                         }
592                         //MS OS Descriptor Query
593                         case 0xee:
594                         {
595                             USB_queue_EP0(MSOSDescriptor, sizeof(MSOSDescriptor), USB_transaction.wLength);
596                             break;
597                         }                       
598                         break;
599                     }  
600                     break;
601                 }
602                 
603                 //Device Qualifier
604                 case 0x6: 
605                 {          
606                     USB_queue_EP0(device_qualifier, sizeof(device_qualifier), USB_transaction.wLength);
607                     break;
608                 }                        
609             }
610             break;
611         }
612         
613         // Set configuration
614         case 0x9: 
615         {
616             //Enumeration complete!
617             break;
618         }
619         
620         default: 
621         {
622             USBE0CSR0bits.STALL = 1;
623             break;
624         }  
625     }
626 }
627 
628 //USB_queue_EP0(config_descriptor, sizeof(config_descriptor), USB_transaction.wLength);
629 void USB_queue_EP0(uint8_t *buffer, int size, int max_size)
630 {
631     int cnt;
632     
633     if (max_size < size)
634         size = max_size;
635     
636     EP[0].tx_num_bytes = size;
637     
638     for (cnt = 0; cnt < size; cnt++)
639     {
640         EP[0].tx_buffer[cnt] = buffer[cnt];
641     }       
642     
643     EP0_TX();
644 }
645 
646 // Send EP[0].tx_num_bytes from EP[0].tx_buffer on endpoint 0
647 void EP0_TX()
648 {    
649      int cnt = 0;
650     
651     //a pointer
652     uint8_t *FIFO_buffer;
653 
654     //load the pointer with the address of the TX buffer
655     FIFO_buffer = (uint8_t *)&USBFIFO0;
656     
657     //return if the TX buffer is empty
658     if (EP0_Wait_TXRDY())
659     {
660         return;
661     }
662         
663     //send data until the TX buffer is empty
664     while (cnt < EP[0].tx_num_bytes)
665     {
666         *FIFO_buffer = EP[0].tx_buffer[cnt]; // Send the bytes
667 
668         cnt++;
669         
670         // Have we sent 64 bytes?
671         if ((cnt > 0) && (cnt % 64 == 0))
672         {
673             //Set TXRDY and wait for it to be cleared before sending any more bytes
674             USBE0CSR0bits.TXRDY = 1; 
675             
676             //wait while the data is being sent
677             while(!EP0_Wait_TXRDY());
678         }   
679         
680         //if the descriptor is larger than the 64 byte buffer size
681         //then send another chunk
682         if ((cnt > 0) && (cnt % 64 == 0))
683         {
684             //Set TXRDY and wait for it to be cleared before sending any more bytes
685             USBE0CSR0bits.TXRDY = 1;            
686             if(EP0_Wait_TXRDY())
687             {
688                 return;
689             }            
690         }
691     }
692 
693     USBE0CSR0bits.TXRDY = 1;            
694 }
695 
696 void EP0_RX(int length)
697 {
698     int cnt;
699     uint8_t *FIFO_buffer;
700     
701     // Store number of bytes received
702     EP[0].rx_num_bytes = USBE0CSR2bits.RXCNT;
703     
704     // Get 8-bit pointer to USB FIFO for endpoint 0
705     FIFO_buffer = (uint8_t *)&USBFIFO0;
706     
707     for(cnt = 0; cnt < length; cnt++)
708     {
709         // Read in one byte at a time
710         EP[0].rx_buffer[cnt] = *(FIFO_buffer + (cnt & 3));
711     }
712      
713     USBE0CSR0bits.RXRDYC = 1;
714 }
715 
716 int EP0_Wait_TXRDY()
717 {
718     int timeout;
719     
720     timeout = 0;
721     
722     while (USBE0CSR0bits.TXRDY)
723     {
724         timeout++;
725         
726         if (timeout > 5000)
727         {
728             return 1;
729         }
730     };
731     
732     return 0;
733 }
734 
735 int EP2_Wait_TXRDY()
736 {
737     int timeout;
738     
739     timeout = 0;
740     
741     while (USBE1CSR0bits.TXPKTRDY)
742     {
743         timeout++;
744         
745         if (timeout > 5000)
746         {
747             return 1;
748         }
749     };
750     
751     return 0;
752 }
753