| /* Teensy Rebootor |
| * http://www.pjrc.com/teensy/ |
| * Copyright (c) 2010 PJRC.COM, LLC |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above description, website URL and copyright notice and this permission |
| * notice shall be included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| */ |
| |
| // Version 1.0: Initial Release |
| |
| #define USB_PRIVATE_INCLUDE |
| #include "usb.h" |
| #include <util/delay.h> |
| |
| /************************************************************************** |
| * |
| * Configurable Options |
| * |
| **************************************************************************/ |
| |
| #define STR_MANUFACTURER L"PJRC" |
| #define STR_PRODUCT L"Rebootor" |
| #define VENDOR_ID 0x16C0 |
| #define PRODUCT_ID 0x0477 |
| #define RX_SIZE 6 // receive packet size |
| |
| |
| /************************************************************************** |
| * |
| * Endpoint Buffer Configuration |
| * |
| **************************************************************************/ |
| |
| #define ENDPOINT0_SIZE 32 |
| |
| |
| static const uint8_t PROGMEM endpoint_config_table[] = { |
| 1, EP_TYPE_INTERRUPT_IN, EP_SIZE(8) | EP_SINGLE_BUFFER, |
| 0, |
| 0, |
| 0 |
| }; |
| |
| |
| /************************************************************************** |
| * |
| * Descriptor Data |
| * |
| **************************************************************************/ |
| |
| // Descriptors are the data that your computer reads when it auto-detects |
| // this USB device (called "enumeration" in USB lingo). The most commonly |
| // changed items are editable at the top of this file. Changing things |
| // in here should only be done by those who've read chapter 9 of the USB |
| // spec and relevant portions of any USB class specifications! |
| |
| |
| static uint8_t PROGMEM device_descriptor[] = { |
| 18, // bLength |
| 1, // bDescriptorType |
| 0x00, 0x02, // bcdUSB |
| 0, // bDeviceClass |
| 0, // bDeviceSubClass |
| 0, // bDeviceProtocol |
| ENDPOINT0_SIZE, // bMaxPacketSize0 |
| LSB(VENDOR_ID), MSB(VENDOR_ID), // idVendor |
| LSB(PRODUCT_ID), MSB(PRODUCT_ID), // idProduct |
| 0x00, 0x01, // bcdDevice |
| 1, // iManufacturer |
| 2, // iProduct |
| 0, // iSerialNumber |
| 1 // bNumConfigurations |
| }; |
| |
| static uint8_t PROGMEM rawhid_hid_report_desc[] = { |
| 0x06, 0x00, 0xFF, |
| 0x0A, 0x00, 0x01, |
| 0xA1, 0x01, // Collection 0x01 |
| 0x75, 0x08, // report size = 8 bits |
| 0x15, 0x00, // logical minimum = 0 |
| 0x26, 0xFF, 0x00, // logical maximum = 255 |
| 0x95, RX_SIZE, // report count |
| 0x09, 0x02, // usage |
| 0x91, 0x02, // Output (array) |
| 0xC0 // end collection |
| }; |
| |
| |
| #define CONFIG1_DESC_SIZE (9+9+9+7) |
| #define RAWHID_HID_DESC_OFFSET (9+9) |
| static uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = { |
| // configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10 |
| 9, // bLength; |
| 2, // bDescriptorType; |
| LSB(CONFIG1_DESC_SIZE), // wTotalLength |
| MSB(CONFIG1_DESC_SIZE), |
| 1, // bNumInterfaces |
| 1, // bConfigurationValue |
| 0, // iConfiguration |
| 0xC0, // bmAttributes |
| 50, // bMaxPower |
| |
| // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 |
| 9, // bLength |
| 4, // bDescriptorType |
| 0, // bInterfaceNumber |
| 0, // bAlternateSetting |
| 1, // bNumEndpoints |
| 0x03, // bInterfaceClass (0x03 = HID) |
| 0x00, // bInterfaceSubClass |
| 0x00, // bInterfaceProtocol |
| 0, // iInterface |
| // HID interface descriptor, HID 1.11 spec, section 6.2.1 |
| 9, // bLength |
| 0x21, // bDescriptorType |
| 0x11, 0x01, // bcdHID |
| 0, // bCountryCode |
| 1, // bNumDescriptors |
| 0x22, // bDescriptorType |
| sizeof(rawhid_hid_report_desc), // wDescriptorLength |
| 0, |
| // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 |
| 7, // bLength |
| 5, // bDescriptorType |
| 1 | 0x80, // bEndpointAddress |
| 0x03, // bmAttributes (0x03=intr) |
| 8, 0, // wMaxPacketSize |
| 0x80 // bInterval |
| }; |
| |
| // If you're desperate for a little extra code memory, these strings |
| // can be completely removed if iManufacturer, iProduct, iSerialNumber |
| // in the device desciptor are changed to zeros. |
| struct usb_string_descriptor_struct { |
| uint8_t bLength; |
| uint8_t bDescriptorType; |
| int16_t wString[]; |
| }; |
| static struct usb_string_descriptor_struct PROGMEM string0 = { |
| 4, |
| 3, |
| {0x0409} |
| }; |
| static struct usb_string_descriptor_struct PROGMEM string1 = { |
| sizeof(STR_MANUFACTURER), |
| 3, |
| STR_MANUFACTURER |
| }; |
| static struct usb_string_descriptor_struct PROGMEM string2 = { |
| sizeof(STR_PRODUCT), |
| 3, |
| STR_PRODUCT |
| }; |
| |
| // This table defines which descriptor data is sent for each specific |
| // request from the host (in wValue and wIndex). |
| static struct descriptor_list_struct { |
| uint16_t wValue; |
| uint16_t wIndex; |
| const uint8_t *addr; |
| uint8_t length; |
| } PROGMEM descriptor_list[] = { |
| {0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)}, |
| {0x0200, 0x0000, config1_descriptor, sizeof(config1_descriptor)}, |
| {0x2200, 0, rawhid_hid_report_desc, sizeof(rawhid_hid_report_desc)}, |
| {0x2100, 0, config1_descriptor+RAWHID_HID_DESC_OFFSET, 9}, |
| {0x0300, 0x0000, (const uint8_t *)&string0, 4}, |
| {0x0301, 0x0409, (const uint8_t *)&string1, sizeof(STR_MANUFACTURER)}, |
| {0x0302, 0x0409, (const uint8_t *)&string2, sizeof(STR_PRODUCT)} |
| }; |
| #define NUM_DESC_LIST (sizeof(descriptor_list)/sizeof(struct descriptor_list_struct)) |
| |
| |
| /************************************************************************** |
| * |
| * Variables - these are the only non-stack RAM usage |
| * |
| **************************************************************************/ |
| |
| // zero when we are not configured, non-zero when enumerated |
| static volatile uint8_t usb_configuration=0; |
| |
| |
| |
| /************************************************************************** |
| * |
| * Public Functions - these are the API intended for the user |
| * |
| **************************************************************************/ |
| |
| |
| // initialize USB |
| void usb_init(void) |
| { |
| HW_CONFIG(); |
| USB_FREEZE(); // enable USB |
| PLL_CONFIG(); // config PLL |
| while (!(PLLCSR & (1<<PLOCK))) ; // wait for PLL lock |
| USB_CONFIG(); // start USB clock |
| UDCON = 0; // enable attach resistor |
| usb_configuration = 0; |
| UDIEN = (1<<EORSTE); |
| sei(); |
| } |
| |
| |
| |
| /************************************************************************** |
| * |
| * Private Functions - not intended for general user consumption.... |
| * |
| **************************************************************************/ |
| |
| |
| #if (GCC_VERSION >= 40300) && (GCC_VERSION < 40302) |
| #error "Buggy GCC 4.3.0 compiler, please upgrade!" |
| #endif |
| |
| |
| // USB Device Interrupt - handle all device-level events |
| // the transmit buffer flushing is triggered by the start of frame |
| // |
| ISR(USB_GEN_vect) |
| { |
| uint8_t intbits; |
| |
| intbits = UDINT; |
| UDINT = 0; |
| if (intbits & (1<<EORSTI)) { |
| UENUM = 0; |
| UECONX = 1; |
| UECFG0X = EP_TYPE_CONTROL; |
| UECFG1X = EP_SIZE(ENDPOINT0_SIZE) | EP_SINGLE_BUFFER; |
| UEIENX = (1<<RXSTPE); |
| usb_configuration = 0; |
| } |
| } |
| |
| |
| |
| // Misc functions to wait for ready and send/receive packets |
| static inline void usb_wait_in_ready(void) |
| { |
| while (!(UEINTX & (1<<TXINI))) ; |
| } |
| static inline void usb_send_in(void) |
| { |
| UEINTX = ~(1<<TXINI); |
| } |
| static inline void usb_wait_receive_out(void) |
| { |
| while (!(UEINTX & (1<<RXOUTI))) ; |
| } |
| static inline void usb_ack_out(void) |
| { |
| UEINTX = ~(1<<RXOUTI); |
| } |
| |
| |
| |
| // USB Endpoint Interrupt - endpoint 0 is handled here. The |
| // other endpoints are manipulated by the user-callable |
| // functions, and the start-of-frame interrupt. |
| // |
| ISR(USB_COM_vect) |
| { |
| uint8_t intbits; |
| const uint8_t *list; |
| const uint8_t *cfg; |
| uint8_t i, n, len, en; |
| uint8_t bmRequestType; |
| uint8_t bRequest; |
| uint16_t wValue; |
| uint16_t wIndex; |
| uint16_t wLength; |
| uint16_t desc_val; |
| const uint8_t *desc_addr; |
| uint8_t desc_length; |
| |
| UENUM = 0; |
| intbits = UEINTX; |
| if (intbits & (1<<RXSTPI)) { |
| bmRequestType = UEDATX; |
| bRequest = UEDATX; |
| wValue = UEDATX; |
| wValue |= (UEDATX << 8); |
| wIndex = UEDATX; |
| wIndex |= (UEDATX << 8); |
| wLength = UEDATX; |
| wLength |= (UEDATX << 8); |
| UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI)); |
| if (bRequest == GET_DESCRIPTOR) { |
| list = (const uint8_t *)descriptor_list; |
| for (i=0; ; i++) { |
| if (i >= NUM_DESC_LIST) { |
| UECONX = (1<<STALLRQ)|(1<<EPEN); //stall |
| return; |
| } |
| desc_val = pgm_read_word(list); |
| if (desc_val != wValue) { |
| list += sizeof(struct descriptor_list_struct); |
| continue; |
| } |
| list += 2; |
| desc_val = pgm_read_word(list); |
| if (desc_val != wIndex) { |
| list += sizeof(struct descriptor_list_struct)-2; |
| continue; |
| } |
| list += 2; |
| desc_addr = (const uint8_t *)pgm_read_word(list); |
| list += 2; |
| desc_length = pgm_read_byte(list); |
| break; |
| } |
| len = (wLength < 256) ? wLength : 255; |
| if (len > desc_length) len = desc_length; |
| do { |
| // wait for host ready for IN packet |
| do { |
| i = UEINTX; |
| } while (!(i & ((1<<TXINI)|(1<<RXOUTI)))); |
| if (i & (1<<RXOUTI)) return; // abort |
| // send IN packet |
| n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE; |
| for (i = n; i; i--) { |
| UEDATX = pgm_read_byte(desc_addr++); |
| } |
| len -= n; |
| usb_send_in(); |
| } while (len || n == ENDPOINT0_SIZE); |
| return; |
| } |
| if (bRequest == SET_ADDRESS) { |
| usb_send_in(); |
| usb_wait_in_ready(); |
| UDADDR = wValue | (1<<ADDEN); |
| return; |
| } |
| if (bRequest == SET_CONFIGURATION && bmRequestType == 0) { |
| usb_configuration = wValue; |
| usb_send_in(); |
| cfg = endpoint_config_table; |
| for (i=1; i<5; i++) { |
| UENUM = i; |
| en = pgm_read_byte(cfg++); |
| UECONX = en; |
| if (en) { |
| UECFG0X = pgm_read_byte(cfg++); |
| UECFG1X = pgm_read_byte(cfg++); |
| } |
| } |
| UERST = 0x1E; |
| UERST = 0; |
| return; |
| } |
| if (bRequest == GET_CONFIGURATION && bmRequestType == 0x80) { |
| usb_wait_in_ready(); |
| UEDATX = usb_configuration; |
| usb_send_in(); |
| return; |
| } |
| |
| if (bRequest == GET_STATUS) { |
| usb_wait_in_ready(); |
| i = 0; |
| if (bmRequestType == 0x82) { |
| UENUM = wIndex; |
| if (UECONX & (1<<STALLRQ)) i = 1; |
| UENUM = 0; |
| } |
| UEDATX = i; |
| UEDATX = 0; |
| usb_send_in(); |
| return; |
| } |
| if ((bRequest == CLEAR_FEATURE || bRequest == SET_FEATURE) |
| && bmRequestType == 0x02 && wValue == 0) { |
| i = wIndex & 0x7F; |
| if (i >= 1 && i <= MAX_ENDPOINT) { |
| usb_send_in(); |
| UENUM = i; |
| if (bRequest == SET_FEATURE) { |
| UECONX = (1<<STALLRQ)|(1<<EPEN); |
| } else { |
| UECONX = (1<<STALLRQC)|(1<<RSTDT)|(1<<EPEN); |
| UERST = (1 << i); |
| UERST = 0; |
| } |
| return; |
| } |
| } |
| if (wIndex == 0) { |
| if (bmRequestType == 0x21 && bRequest == HID_SET_REPORT) { |
| uint8_t buf[6]; |
| len = RX_SIZE; |
| do { |
| n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE; |
| usb_wait_receive_out(); |
| buf[0] = UEDATX; |
| buf[1] = UEDATX; |
| buf[2] = UEDATX; |
| buf[3] = UEDATX; |
| buf[4] = UEDATX; |
| buf[5] = UEDATX; |
| if (buf[0] == 'r' && buf[1] == 'e' && buf[2] == 'b' |
| && buf[3] == 'o' && buf[4] == 'o' && buf[5] == 't') { |
| // When we get the "reboot" message, |
| // pulse all port B, C, & D pins low |
| PORTB = 0, PORTC = 0, PORTD = 0; |
| DDRB = 0xFF, DDRC = 0xFF, DDRD = 0xFF; |
| _delay_us(25); |
| DDRB = 0, DDRC = 0, DDRD = 0; |
| PORTB = 0xFF, PORTC = 0xFF, PORTD = 0xFF; |
| //DDRD = 0x40; |
| //PORTD = 0x40; |
| } |
| // ignore incoming bytes |
| usb_ack_out(); |
| len -= n; |
| } while (len); |
| usb_wait_in_ready(); |
| usb_send_in(); |
| return; |
| } |
| } |
| } |
| UECONX = (1<<STALLRQ) | (1<<EPEN); // stall |
| } |
| |
| |