Files
retrobsd/sys/pic32/usb_boot.c
Sergey 64430a0f91 Added comfiguration for Majenko SDXL board.
DIP and Pinguino-micro boards removed - nobody uses these.
Deleted unused linker scripts.
2014-10-04 13:57:03 -07:00

843 lines
25 KiB
C

/*
* USB HID bootloader for PIC32 microcontroller.
*
* Based on Microchip sources.
* Heavily rewritten by Serge Vakulenko, <serge@vak.ru>.
*
* The software supplied herewith by Microchip Technology Incorporated
* (the 'Company') for its PIC(R) Microcontroller is intended and
* supplied to you, the Company's customer, for use solely and
* exclusively on Microchip PIC Microcontroller products. The
* software is owned by the Company and/or its supplier, and is
* protected under applicable copyright laws. All rights are reserved.
* Any use in violation of the foregoing restrictions may subject the
* user to criminal sanctions under applicable laws, as well as to
* civil liability for the breach of the terms and conditions of this license.
*
* THIS SOFTWARE IS PROVIDED IN AN 'AS IS' CONDITION. NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
* TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
* IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
*/
#include <machine/usb_device.h>
#include <machine/usb_function_hid.h>
#include <machine/pic32mx.h>
#include <machine/io.h>
/*
* Flash memory.
*/
#define FLASH_BASE 0x1D000000 /* physical */
#ifndef FLASH_USER
# define FLASH_USER FLASH_BASE /* physical: beginning of user application */
#endif
#ifndef FLASH_JUMP /* virtual: start of application */
# define FLASH_JUMP (FLASH_USER + 0x80001000)
#endif
#define FLASH_PAGESZ 4096 /* bytes per page, for erasing */
/*
* Commands of bootloader protocol.
*/
#define QUERY_DEVICE 0x02 // Command that the host uses to learn
// about the device (what regions can be
// programmed, and what type of memory
// is the region)
#define UNLOCK_CONFIG 0x03 // Note, this command is used for both
// locking and unlocking the config bits
#define ERASE_DEVICE 0x04 // Host sends this command to start
// an erase operation. Firmware controls
// which pages should be erased.
#define PROGRAM_DEVICE 0x05 // If host is going to send a full
// REQUEST_SIZE to be programmed, it
// uses this command.
#define PROGRAM_COMPLETE 0x06 // If host send less than a REQUEST_SIZE
// to be programmed, or if it wished
// to program whatever was left in
// the buffer, it uses this command.
#define GET_DATA 0x07 // The host sends this command in order
// to read out memory from the device.
// Used during verify (and read/export
// hex operations)
#define RESET_DEVICE 0x08 // Resets the microcontroller, so it can
// update the config bits (if they were
// programmed, and so as to leave
// the bootloader (and potentially go back
// into the main application)
/*
* HID packet structure.
*/
#define PACKET_SIZE 64 // HID packet size
#define REQUEST_SIZE 56 // Number of bytes of a standard request
typedef union __attribute__ ((packed))
{
unsigned char Contents [PACKET_SIZE];
struct __attribute__ ((packed)) {
unsigned char Command;
unsigned Address;
unsigned char Size;
unsigned char PadBytes [PACKET_SIZE - 6 - REQUEST_SIZE];
unsigned int Data [REQUEST_SIZE / sizeof(unsigned)];
};
struct __attribute__ ((packed)) {
unsigned char Command;
unsigned char PacketDataFieldSize;
unsigned char DeviceFamily;
unsigned char Type1;
unsigned long Address1;
unsigned long Length1;
unsigned char Type2;
unsigned long Address2;
unsigned long Length2;
unsigned char Type3;
unsigned long Address3;
unsigned long Length3;
unsigned char Type4; // End of sections list indicator goes here, fill with 0xFF.
unsigned char ExtraPadBytes[33];
} query;
} packet_t;
static packet_t send; // 64-byte send buffer (EP1 IN to the PC)
static packet_t receive_buffer; // 64-byte receive buffer (EP1 OUT from the PC)
static packet_t receive; // copy of receive buffer, for processing
static USB_HANDLE request_handle;
/*
* Placed at fixed address 0x80000000 by linker.
* To enter boot mode from user program, set this value
* to 0x12345678 and perform a software reset.
*/
unsigned int reset_key
__attribute__((section(".sdata")));
static unsigned int buf [32];
static unsigned int buf_index;
static unsigned int base_address;
/*
* GPIO pin control.
*/
#define TRIS_VAL(p) (&p)[0]
#define TRIS_CLR(p) (&p)[1]
#define TRIS_SET(p) (&p)[2]
#define TRIS_INV(p) (&p)[3]
#define PORT_VAL(p) (&p)[4]
#define PORT_CLR(p) (&p)[5]
#define PORT_SET(p) (&p)[6]
#define PORT_INV(p) (&p)[7]
#define LAT_VAL(p) (&p)[8]
#define LAT_CLR(p) (&p)[9]
#define LAT_SET(p) (&p)[10]
#define LAT_INV(p) (&p)[11]
/*
* Boot code.
*/
asm (" .section .startup,\"ax\",@progbits");
asm (" .globl _start");
asm (" .type _start, function");
asm ("_start: la $sp, _estack");
asm (" la $ra, main");
asm (" la $gp, _gp");
asm (" jr $ra");
asm (" .text");
/*
* A single button is used to control the bootloader mode.
* Configured by parameters:
* BL_BUTTON_PORT = TRISA ... TRISG
* BL_BUTTON_PIN = 0 ... 15
*/
static inline void button_init()
{
TRIS_SET(BL_BUTTON_PORT) = 1 << BL_BUTTON_PIN;
}
static inline int button_pressed()
{
return ! (PORT_VAL(BL_BUTTON_PORT) & (1 << BL_BUTTON_PIN));
}
/*
* Up to three LEDs can be used to indicate a bootloader mode.
* Configured by parameters:
* - first LED: BL_LED_PORT, BL_LED_PIN, BL_LED_INVERT
* - second LED: BL_LED2_PORT, BL_LED2_PIN, BL_LED2_INVERT
* - third LED: BL_LED3_PORT, BL_LED3_PIN, BL_LED3_INVERT
*
* Additionally, up to two output signals can be set or cleared at startup.
* Configured by parameters:
* BL_SET_PORT, BL_SET_PIN
* BL_SET2_PORT, BL_SET2_PIN
* BL_CLEAR_PORT, BL_CLEAR_PIN
* BL_CLEAR2_PORT, BL_CLEAR2_PIN
*
* Settings for UBW32 board:
* - first LED is E2, inverted
* - second LED is E3
* - set signals E0 and E1
*
* For Maximite board:
* - LED is E1, inverted
* - clear signal F0
*
* For eflightworks DIP board:
* - first LED is E6, inverted
* - second LED is E7
* - clear signals E4 and E5
*
* For PIC32 Starter Kit board:
* - first LED is D0
* - second LED is D1, inverted
* - third LED is D2, inverted
*/
static inline void led_init()
{
/* First LED. */
#ifdef BL_LED_INVERT
LAT_SET(BL_LED_PORT) = 1 << BL_LED_PIN;
#else
LAT_CLR(BL_LED_PORT) = 1 << BL_LED_PIN;
#endif
TRIS_CLR(BL_LED_PORT) = 1 << BL_LED_PIN;
/* Optional second LED. */
#ifdef BL_LED2_PORT
#ifdef BL_LED2_INVERT
LAT_SET(BL_LED2_PORT) = 1 << BL_LED2_PIN;
#else
LAT_CLR(BL_LED2_PORT) = 1 << BL_LED2_PIN;
#endif
TRIS_CLR(BL_LED2_PORT) = 1 << BL_LED2_PIN;
#endif
/* Optional third LED. */
#ifdef BL_LED3_PORT
#ifdef BL_LED3_INVERT
LAT_SET(BL_LED3_PORT) = 1 << BL_LED3_PIN;
#else
LAT_CLR(BL_LED3_PORT) = 1 << BL_LED3_PIN;
#endif
TRIS_CLR(BL_LED3_PORT) = 1 << BL_LED3_PIN;
#endif
/* Additional signals. */
#ifdef BL_SET_PORT
LAT_SET(BL_SET_PORT) = 1 << BL_SET_PIN;
TRIS_CLR(BL_SET_PORT) = 1 << BL_SET_PIN;
#endif
#ifdef BL_SET2_PORT
LAT_SET(BL_SET2_PORT) = 1 << BL_SET2_PIN;
TRIS_CLR(BL_SET2_PORT) = 1 << BL_SET2_PIN;
#endif
#ifdef BL_CLEAR_PORT
LAT_CLR(BL_CLEAR_PORT) = 1 << BL_CLEAR_PIN;
TRIS_CLR(BL_CLEAR_PORT) = 1 << BL_CLEAR_PIN;
#endif
#ifdef BL_CLEAR2_PORT
LAT_CLR(BL_CLEAR2_PORT) = 1 << BL_CLEAR2_PIN;
TRIS_CLR(BL_CLEAR2_PORT) = 1 << BL_CLEAR2_PIN;
#endif
}
static inline void led_toggle()
{
/* First LED. */
LAT_INV(BL_LED_PORT) = 1 << BL_LED_PIN;
/* Optional second LED. */
#ifdef BL_LED2_PORT
LAT_INV(BL_LED2_PORT) = 1 << BL_LED2_PIN;
#endif
/* Optional third LED. */
#ifdef BL_LED3_PORT
LAT_INV(BL_LED3_PORT) = 1 << BL_LED3_PIN;
#endif
}
#if 0
/*
* Printing to UART.
* These functions are useful for debugging a bootloader.
*/
static inline void cinit()
{
/*
* Setup UART registers.
* Compute the divisor for 115.2 kbaud.
*/
U1BRG = PIC32_BRG_BAUD (CPU_KHZ * 1000, 115200);
U1STA = 0;
U1MODE = PIC32_UMODE_PDSEL_8NPAR | /* 8-bit data, no parity */
PIC32_UMODE_ON; /* UART Enable */
U1STASET = PIC32_USTA_URXEN | /* Receiver Enable */
PIC32_USTA_UTXEN; /* Transmit Enable */
}
/*
* Send a byte to the UART transmitter.
*/
static void cputc (int c)
{
/* Wait for transmitter shift register empty. */
while (! (U1STA & PIC32_USTA_TRMT))
continue;
again:
/* Send byte. */
U1TXREG = c;
/* Wait for transmitter shift register empty. */
while (! (U1STA & PIC32_USTA_TRMT))
continue;
if (c == '\n') {
c = '\r';
goto again;
}
}
static void cputs (const char *p)
{
for (; *p; ++p)
cputc (*p);
}
#endif
/*
* Reset the microrontroller.
*/
static void soft_reset()
{
mips_intr_disable();
if (! (DMACON & 0x1000)) {
DMACONSET = 0x1000;
while (DMACON & 0x800)
continue;
}
SYSKEY = 0;
SYSKEY = 0xaa996655;
SYSKEY = 0x556699aa;
RSWRSTSET = 1;
(void) RSWRST;
for (;;)
continue;
}
/*
* Microsecond delay routine for MIPS processor.
*/
static void udelay (unsigned usec)
{
unsigned now = mips_read_c0_register (C0_COUNT, 0);
unsigned final = now + usec * (CPU_KHZ / 1000);
for (;;) {
now = mips_read_c0_register (C0_COUNT, 0);
/* This comparison is valid only when using a signed type. */
if ((int) (now - final) >= 0)
break;
}
}
/*
* Clear an array.
*/
void memzero (void *data, unsigned nbytes)
{
unsigned *wordp = (unsigned*) data;
unsigned nwords = nbytes / sizeof(int);
while (nwords-- > 0)
*wordp++ = 0;
}
/*
* Copy an array.
*/
void memcopy (void *from, void *to, unsigned nbytes)
{
unsigned *src = (unsigned*) from;
unsigned *dst = (unsigned*) to;
unsigned nwords = nbytes / sizeof(int);
while (nwords-- > 0)
*dst++ = *src++;
}
/*
* BlinkUSBStatus turns on and off LEDs
* corresponding to the USB device state.
*/
static void led_blink()
{
static unsigned led_count;
if (led_count == 0)
led_count = 100000;
led_count--;
if (! usb_is_device_suspended() &&
usb_device_state == CONFIGURED_STATE &&
led_count == 0)
{
led_toggle();
}
}
/*
* Perform non-volatile memory operation.
*/
static void nvm_operation (unsigned op, unsigned address, unsigned data)
{
int x;
// Convert virtual address to physical
NVMADDR = address & 0x1fffffff;
NVMDATA = data;
// Disable interrupts
x = mips_intr_disable();
// Enable Flash Write/Erase Operations
NVMCON = PIC32_NVMCON_WREN | op;
// Data sheet prescribes 6us delay for LVD to become stable.
// To be on the safer side, we shall set 7us delay.
udelay (7);
NVMKEY = 0xAA996655;
NVMKEY = 0x556699AA;
NVMCONSET = PIC32_NVMCON_WR;
// Wait for WR bit to clear
while (NVMCON & PIC32_NVMCON_WR)
continue;
// Disable Flash Write/Erase operations
NVMCONCLR = PIC32_NVMCON_WREN;
// Enable interrupts
mips_intr_restore (x);
#if 0
// Check error status
if (NVMCON & (PIC32_NVMCON_WRERR | PIC32_NVMCON_LVDERR)) {
TODO;
}
#endif
}
/*
* Use word writes to write code chunks less than a full 64 byte block size.
*/
static void write_flash_block()
{
unsigned int i;
for (i = 0; buf_index > 0; buf_index--) {
// Write word to flash memory.
nvm_operation (PIC32_NVMCON_WORD_PGM,
base_address - buf_index * sizeof(unsigned),
buf [i++]);
}
}
/*
* A command packet was received from PC.
* Process it and return 1 when a reply is ready.
*/
static int handle_packet()
{
int i;
switch (receive.Command) {
case QUERY_DEVICE:
// Prepare a response packet, which lets the PC software know
// about the memory ranges of this device.
memzero (&send, PACKET_SIZE);
send.query.Command = QUERY_DEVICE;
send.query.PacketDataFieldSize = REQUEST_SIZE;
send.query.DeviceFamily = 3; /* PIC32 */
send.query.Type1 = 1; /* 'program' memory type */
send.query.Address1 = FLASH_USER;
send.query.Length1 = FLASH_BASE + BMXPFMSZ - FLASH_USER;
send.query.Type2 = 0xFF; /* end of list */
return 1;
case UNLOCK_CONFIG:
return 0;
case ERASE_DEVICE:
for (i = 0; i < BMXPFMSZ/FLASH_PAGESZ; i++) {
/* Erase flash page */
nvm_operation (PIC32_NVMCON_PAGE_ERASE,
FLASH_USER + i * FLASH_PAGESZ, 0);
// Call this function periodically to prevent falling
// off the bus if any SETUP packets should happen to arrive.
usb_device_tasks();
}
// Good practice to clear WREN bit anytime we are
// not expecting to do erase/write operations,
// further reducing probability of accidental activation.
NVMCONCLR = PIC32_NVMCON_WREN;
return 0;
case PROGRAM_DEVICE:
if (base_address == 0)
base_address = receive.Address;
if (base_address == receive.Address) {
for (i = 0; i < receive.Size/sizeof(unsigned); i++) {
unsigned int index;
index = (REQUEST_SIZE - receive.Size) / sizeof(unsigned) + i;
// Data field is right justified.
// Need to put it in the buffer left justified.
buf [buf_index++] = receive.Data [index];
base_address += sizeof(unsigned);
if (buf_index == REQUEST_SIZE / sizeof(unsigned)) {
write_flash_block();
}
}
}
// else host sent us a non-contiguous packet address...
// to make this firmware simpler, host should not do this without
// sending a PROGRAM_COMPLETE command in between program sections.
return 0;
case PROGRAM_COMPLETE:
write_flash_block();
// Reinitialize pointer to an invalid range, so we know the next
// PROGRAM_DEVICE will be the start address of a contiguous section.
base_address = 0;
return 0;
case GET_DATA:
memzero (&send, PACKET_SIZE);
send.Command = GET_DATA;
send.Address = receive.Address;
send.Size = receive.Size;
memcopy ((void*) (receive.Address | 0x80000000),
send.Data + (REQUEST_SIZE - receive.Size)/sizeof(unsigned),
receive.Size);
return 1;
case RESET_DEVICE:
// Disable USB module
U1CON = 0x0000;
// And wait awhile for the USB cable capacitance to discharge
// down to disconnected (SE0) state. Otherwise host might not
// realize we disconnected/reconnected when we do the reset.
udelay (1000);
soft_reset();
return 0;
}
return 0;
}
/*
* Main program entry point.
*/
int main()
{
// IM: Quick fix to enable a smooth start @120MHz
/* Wait until both System and USB PLL are locked */
// while ( (OSCCON & 0x60) != 0x60 ); // does not work
while(!(OSCCON & 0x60));
/* Initialize STATUS register: master interrupt disable. */
mips_write_c0_register (C0_STATUS, 0, ST_CU0 | ST_BEV);
/* Clear CAUSE register: use special interrupt vector 0x200. */
mips_write_c0_register (C0_CAUSE, 0, CA_IV);
/*
* Setup wait states.
*/
CHECON = 2;
BMXCONCLR = 0x40;
CHECONSET = 0x30;
/* Disable JTAG port, to use it for i/o. */
DDPCON = 0;
/* Config register: enable kseg0 caching. */
mips_write_c0_register (C0_CONFIG, 0,
mips_read_c0_register (C0_CONFIG, 0) | 3);
mips_ehb();
/* Initialize all .bss variables by zeros. */
extern unsigned char __bss_start, __bss_end;
memzero (&__bss_start, &__bss_end - &__bss_start);
//cinit();
//cputs("=0=\n");
// Initialize all of the push buttons
button_init();
/*
* To call the normal application (i.e. don't go into the bootloader)
* we need to make sure that the PRG button is not pressed, and that we
* don't have a software reset.
*/
if (! button_pressed() &&
(! (RCON & PIC32_RCON_SWR) || reset_key != 0x12345678))
{
// Must clear out software key
reset_key = 0;
// If there is NO real program to execute, then fall through to the bootloader
if (*(int*) FLASH_JUMP != 0xFFFFFFFF) {
void (*fptr)(void) = (void (*)(void)) FLASH_JUMP;
fptr();
for (;;)
continue;
}
}
// Must clear out software key.
reset_key = 0;
RCONCLR = PIC32_RCON_SWR;
// Initialize all of the LED pins
led_init();
// Initialize USB module SFRs and firmware variables to known states.
usb_device_init();
USB_HANDLE reply_handle = 0;
int packet_received = 0;
for (;;) {
// Check bus status and service USB interrupts.
usb_device_tasks();
// Blink the LEDs according to the USB device status
led_blink();
// User Application USB tasks
if (usb_device_state < CONFIGURED_STATE || usb_is_device_suspended())
continue;
// Are we done sending the last response. We need to be before we
// receive the next command because we clear the send buffer
// once we receive a command
if (reply_handle != 0) {
if (usb_handle_busy (reply_handle))
continue;
reply_handle = 0;
}
if (packet_received) {
if (handle_packet())
reply_handle = usb_transfer_one_packet (HID_EP,
IN_TO_HOST, &send.Contents[0], PACKET_SIZE);
packet_received = 0;
continue;
}
// Did we receive a command?
if (usb_handle_busy (request_handle))
continue;
// Make a copy of received data.
memcopy (&receive_buffer, &receive, PACKET_SIZE);
packet_received = 1;
// Restart receiver, to be ready for a next packet.
request_handle = usb_transfer_one_packet (HID_EP, OUT_FROM_HOST,
(unsigned char*) &receive_buffer, PACKET_SIZE);
}
}
/*
* USB Callback Functions
*/
/*
* Process device-specific SETUP requests.
*/
void usbcb_check_other_req()
{
hid_check_request();
}
/*
* This function is called when the device becomes initialized.
* It should initialize the endpoints for the device's usage
* according to the current configuration.
*/
void usbcb_init_ep()
{
// Enable the HID endpoint
usb_enable_endpoint (HID_EP, USB_IN_ENABLED | USB_OUT_ENABLED |
USB_HANDSHAKE_ENABLED | USB_DISALLOW_SETUP);
// Arm the OUT endpoint for the first packet
request_handle = usb_rx_one_packet (HID_EP,
&receive_buffer.Contents[0], PACKET_SIZE);
}
/*
* The device descriptor.
*/
const USB_DEVICE_DESCRIPTOR usb_device = {
sizeof (usb_device), // Size of this descriptor in bytes
USB_DESCRIPTOR_DEVICE, // DEVICE descriptor type
0x0200, // USB Spec Release Number in BCD format
0x00, // Class Code
0x00, // Subclass code
0x00, // Protocol code
USB_EP0_BUFF_SIZE, // Max packet size for EP0
0x04D8, // Vendor ID
0x003C, // Product ID: USB HID Bootloader
0x0002, // Device release number in BCD format
1, // Manufacturer string index
2, // Product string index
0, // Device serial number string index
1, // Number of possible configurations
};
/*
* Configuration 1 descriptor
*/
const unsigned char usb_config1_descriptor[] = {
/*
* Configuration descriptor
*/
sizeof (USB_CONFIGURATION_DESCRIPTOR),
USB_DESCRIPTOR_CONFIGURATION,
0x29, 0x00, // Total length of data for this cfg
1, // Number of interfaces in this cfg
1, // Index value of this configuration
0, // Configuration string index
_DEFAULT | _SELF, // Attributes
50, // Max power consumption (2X mA)
/*
* Interface descriptor
*/
sizeof (USB_INTERFACE_DESCRIPTOR),
USB_DESCRIPTOR_INTERFACE,
0, // Interface Number
0, // Alternate Setting Number
2, // Number of endpoints in this intf
HID_INTF, // Class code
0, // Subclass code
0, // Protocol code
0, // Interface string index
/*
* HID Class-Specific descriptor
*/
sizeof (USB_HID_DSC) + 3,
DSC_HID,
0x11, 0x01, // HID Spec Release Number in BCD format (1.11)
0x00, // Country Code (0x00 for Not supported)
HID_NUM_OF_DSC, // Number of class descriptors
DSC_RPT, // Report descriptor type
HID_RPT01_SIZE, 0x00, // Size of the report descriptor
/*
* Endpoint descriptor
*/
sizeof (USB_ENDPOINT_DESCRIPTOR),
USB_DESCRIPTOR_ENDPOINT,
HID_EP | _EP_IN, // EndpointAddress
_INTERRUPT, // Attributes
PACKET_SIZE, 0, // Size
1, // Interval
/*
* Endpoint descriptor
*/
sizeof (USB_ENDPOINT_DESCRIPTOR),
USB_DESCRIPTOR_ENDPOINT,
HID_EP | _EP_OUT, // EndpointAddress
_INTERRUPT, // Attributes
PACKET_SIZE, 0, // Size
1, // Interval
};
/*
* Class specific descriptor - HID
*/
const unsigned char hid_rpt01 [HID_RPT01_SIZE] = {
0x06, 0x00, 0xFF, // Usage Page = 0xFF00 (Vendor Defined Page 1)
0x09, 0x01, // Usage (Vendor Usage 1)
0xA1, 0x01, // Collection (Application)
0x19, 0x01, // Usage Minimum
0x29, 0x40, // Usage Maximum 64 input usages total (0x01 to 0x40)
0x15, 0x00, // Logical Minimum (data bytes in the report may have minimum value = 0x00)
0x26, 0xFF, 0x00, // Logical Maximum (data bytes in the report may have maximum value = 0x00FF = unsigned 255)
0x75, 0x08, // Report Size: 8-bit field size
0x95, 0x40, // Report Count: Make sixty-four 8-bit fields (the next time the parser hits an "Input", "Output", or "Feature" item)
0x81, 0x00, // Input (Data, Array, Abs): Instantiates input packet fields based on the above report size, count, logical min/max, and usage.
0x19, 0x01, // Usage Minimum
0x29, 0x40, // Usage Maximum 64 output usages total (0x01 to 0x40)
0x91, 0x00, // Output (Data, Array, Abs): Instantiates output packet fields. Uses same report size and count as "Input" fields, since nothing new/different was specified to the parser since the "Input" item.
0xC0, // End Collection
};
/*
* USB Strings
*/
static const USB_STRING_INIT(1) string0_descriptor = {
sizeof(string0_descriptor),
USB_DESCRIPTOR_STRING, /* Language code */
{ 0x0409 },
};
static const USB_STRING_INIT(25) string1_descriptor = {
sizeof(string1_descriptor),
USB_DESCRIPTOR_STRING, /* Manufacturer */
{ 'M','i','c','r','o','c','h','i','p',' ',
'T','e','c','h','n','o','l','o','g','y',
' ','I','n','c','.' },
};
static const USB_STRING_INIT(18) string2_descriptor = {
sizeof(string2_descriptor),
USB_DESCRIPTOR_STRING, /* Product */
{ 'U','S','B',' ','H','I','D',' ','B','o',
'o','t','l','o','a','d','e','r' },
};
/*
* Array of configuration descriptors
*/
const unsigned char *const usb_config[] = {
(const unsigned char *const) &usb_config1_descriptor,
};
/*
* Array of string descriptors
*/
const unsigned char *const usb_string[USB_NUM_STRING_DESCRIPTORS] = {
(const unsigned char *const) &string0_descriptor,
(const unsigned char *const) &string1_descriptor,
(const unsigned char *const) &string2_descriptor,
};