/* Copyright (c) 2011, Peter Barrett ** ** Permission to use, copy, modify, and/or distribute this software for ** any purpose with or without fee is hereby granted, provided that the ** above copyright notice and this permission notice appear in all copies. ** ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ** SOFTWARE. */ #include "USBAPI.h" #include #include #if defined(USBCON) typedef struct { u32 dwDTERate; u8 bCharFormat; u8 bParityType; u8 bDataBits; u8 lineState; } LineInfo; static volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 }; static volatile int32_t breakValue = -1; static u8 wdtcsr_save; #define WEAK __attribute__ ((weak)) extern const CDCDescriptor _cdcInterface PROGMEM; const CDCDescriptor _cdcInterface = { D_IAD(0,2,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,1), // CDC communication interface D_INTERFACE(CDC_ACM_INTERFACE,1,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,0), D_CDCCS(CDC_HEADER,0x10,0x01), // Header (1.10 bcd) D_CDCCS(CDC_CALL_MANAGEMENT,1,1), // Device handles call management (not) D_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT,6), // SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported D_CDCCS(CDC_UNION,CDC_ACM_INTERFACE,CDC_DATA_INTERFACE), // Communication interface is master, data interface is slave 0 D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_ACM),USB_ENDPOINT_TYPE_INTERRUPT,0x10,0x40), // CDC data interface D_INTERFACE(CDC_DATA_INTERFACE,2,CDC_DATA_INTERFACE_CLASS,0,0), D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0), D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ),USB_ENDPOINT_TYPE_BULK,USB_EP_SIZE,0) }; bool isLUFAbootloader() { return pgm_read_word(FLASHEND - 1) == NEW_LUFA_SIGNATURE; } int CDC_GetInterface(u8* interfaceNum) { interfaceNum[0] += 2; // uses 2 return USB_SendControl(TRANSFER_PGM,&_cdcInterface,sizeof(_cdcInterface)); } bool CDC_Setup(USBSetup& setup) { u8 r = setup.bRequest; u8 requestType = setup.bmRequestType; if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType) { if (CDC_GET_LINE_CODING == r) { USB_SendControl(0,(void*)&_usbLineInfo,7); return true; } } if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType) { if (CDC_SEND_BREAK == r) { breakValue = ((uint16_t)setup.wValueH << 8) | setup.wValueL; } if (CDC_SET_LINE_CODING == r) { USB_RecvControl((void*)&_usbLineInfo,7); } if (CDC_SET_CONTROL_LINE_STATE == r) { _usbLineInfo.lineState = setup.wValueL; // auto-reset into the bootloader is triggered when the port, already // open at 1200 bps, is closed. this is the signal to start the watchdog // with a relatively long period so it can finish housekeeping tasks // like servicing endpoints before the sketch ends uint16_t magic_key_pos = MAGIC_KEY_POS; // If we don't use the new RAMEND directly, check manually if we have a newer bootloader. // This is used to keep compatible with the old leonardo bootloaders. // You are still able to set the magic key position manually to RAMEND-1 to save a few bytes for this check. #if MAGIC_KEY_POS != (RAMEND-1) // For future boards save the key in the inproblematic RAMEND // Which is reserved for the main() return value (which will never return) if (isLUFAbootloader()) { // horray, we got a new bootloader! magic_key_pos = (RAMEND-1); } #endif // We check DTR state to determine if host port is open (bit 0 of lineState). if (1200 == _usbLineInfo.dwDTERate && (_usbLineInfo.lineState & 0x01) == 0) { #if MAGIC_KEY_POS != (RAMEND-1) // Backup ram value if its not a newer bootloader and it hasn't already been saved. // This should avoid memory corruption at least a bit, not fully if (magic_key_pos != (RAMEND-1) && *(uint16_t *)magic_key_pos != MAGIC_KEY) { *(uint16_t *)(RAMEND-1) = *(uint16_t *)magic_key_pos; } #endif // Store boot key *(uint16_t *)magic_key_pos = MAGIC_KEY; // Save the watchdog state in case the reset is aborted. wdtcsr_save = WDTCSR; wdt_enable(WDTO_120MS); } else if (*(uint16_t *)magic_key_pos == MAGIC_KEY) { // Most OSs do some intermediate steps when configuring ports and DTR can // twiggle more than once before stabilizing. // To avoid spurious resets we set the watchdog to 120ms and eventually // cancel if DTR goes back high. // Cancellation is only done if an auto-reset was started, which is // indicated by the magic key having been set. wdt_reset(); // Restore the watchdog state in case the sketch was using it. WDTCSR |= (1<