/** * \file * * \brief Main functions for USB composite example * * Copyright (c) 2011-2015 Atmel Corporation. All rights reserved. * * \asf_license_start * * \page License * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name of Atmel may not be used to endorse or promote products derived * from this software without specific prior written permission. * * 4. This software may only be redistributed and used in connection with an * Atmel microcontroller product. * * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * \asf_license_stop * */ // Support and FAQ: visit Atmel Support #ifdef ARDUINO_ARCH_SAM #include #include #include "conf_usb.h" #include "udc.h" #if ENABLED(SDSUPPORT) static volatile bool main_b_msc_enable = false; #endif static volatile bool main_b_cdc_enable = false; static volatile bool main_b_dtr_active = false; void usb_task_idle(void) { #if ENABLED(SDSUPPORT) // Attend SD card access from the USB MSD -- Prioritize access to improve speed int delay = 2; while (main_b_msc_enable && --delay > 0) { if (udi_msc_process_trans()) delay = 10000; // Reset the watchdog, just to be sure REG_WDT_CR = WDT_CR_WDRSTT | WDT_CR_KEY(0xA5); } #endif } #if ENABLED(SDSUPPORT) bool usb_task_msc_enable(void) { return ((main_b_msc_enable = true)); } void usb_task_msc_disable(void) { main_b_msc_enable = false; } bool usb_task_msc_isenabled(void) { return main_b_msc_enable; } #endif bool usb_task_cdc_enable(const uint8_t port) { UNUSED(port); return ((main_b_cdc_enable = true)); } void usb_task_cdc_disable(const uint8_t port) { UNUSED(port); main_b_cdc_enable = false; main_b_dtr_active = false; } bool usb_task_cdc_isenabled(void) { return main_b_cdc_enable; } /*! \brief Called by CDC interface * Callback running when CDC device have received data */ void usb_task_cdc_rx_notify(const uint8_t port) { UNUSED(port); } /*! \brief Configures communication line * * \param cfg line configuration */ static uint16_t dwDTERate = 0; void usb_task_cdc_config(const uint8_t port, usb_cdc_line_coding_t *cfg) { UNUSED(port); // Store last DTE rate dwDTERate = cfg->dwDTERate; } void usb_task_cdc_set_dtr(const uint8_t port, const bool b_enable) { UNUSED(port); // Keep DTR status main_b_dtr_active = b_enable; // Implement Arduino-Compatible kludge to enter programming mode from // the native port: // "Auto-reset into the bootloader is triggered when the port, already // open at 1200 bps, is closed." if (1200 == dwDTERate) { // We check DTR state to determine if host port is open (bit 0 of lineState). if (!b_enable) { // Set RST pin to go low for 65535 clock cycles on reset // This helps restarting when firmware flash ends RSTC->RSTC_MR = 0xA5000F01; // Schedule delayed reset initiateReset(250); } else cancelReset(); } } bool usb_task_cdc_dtr_active(void) { return main_b_dtr_active; } /// Microsoft WCID descriptor typedef struct USB_MicrosoftCompatibleDescriptor_Interface { uint8_t bFirstInterfaceNumber; uint8_t reserved1; uint8_t compatibleID[8]; uint8_t subCompatibleID[8]; uint8_t reserved2[6]; } __attribute__((packed)) USB_MicrosoftCompatibleDescriptor_Interface; typedef struct USB_MicrosoftCompatibleDescriptor { uint32_t dwLength; uint16_t bcdVersion; uint16_t wIndex; uint8_t bCount; uint8_t reserved[7]; USB_MicrosoftCompatibleDescriptor_Interface interfaces[]; } __attribute__((packed)) USB_MicrosoftCompatibleDescriptor; // 3D Printer compatible descriptor static USB_MicrosoftCompatibleDescriptor microsoft_compatible_id_descriptor = { .dwLength = sizeof(USB_MicrosoftCompatibleDescriptor) + 1*sizeof(USB_MicrosoftCompatibleDescriptor_Interface), .bcdVersion = 0x0100, .wIndex = 0x0004, .bCount = 1, .reserved = {0, 0, 0, 0, 0, 0, 0}, .interfaces = { { .bFirstInterfaceNumber = 0, .reserved1 = 1, .compatibleID = "3DPRINT", .subCompatibleID = {0, 0, 0, 0, 0, 0, 0, 0}, .reserved2 = {0, 0, 0, 0, 0, 0}, } } }; #define xstr(s) str(s) #define str(s) #s #define MS3DPRINT_CONFIG u"MS3DPrintConfig" #define MS3DPRINT_CONFIG_DATA \ u"Base=SD\0"\ u"Job3DOutputAreaWidth=" xstr(X_BED_SIZE) "000\0"\ u"Job3DOutputAreaDepth=" xstr(Y_BED_SIZE) "000\0"\ u"Job3DOutputAreaHeight=" xstr(Z_MAX_POS) "000\0"\ u"filamentdiameter=1750\0" typedef struct USB_MicrosoftExtendedPropertiesDescriptor { uint32_t dwLength; uint16_t bcdVersion; uint16_t wIndex; uint16_t bCount; uint32_t dwPropertySize; uint32_t dwPropertyDataType; uint16_t wPropertyNameLength; uint16_t PropertyName[sizeof(MS3DPRINT_CONFIG)/sizeof(uint16_t)]; uint32_t dwPropertyDataLength; uint16_t PropertyData[sizeof(MS3DPRINT_CONFIG_DATA)/sizeof(uint16_t)]; } __attribute__((packed)) USB_MicrosoftExtendedPropertiesDescriptor; static USB_MicrosoftExtendedPropertiesDescriptor microsoft_extended_properties_descriptor = { .dwLength = sizeof(USB_MicrosoftExtendedPropertiesDescriptor), .bcdVersion = 0x0100, .wIndex = 0x0005, .bCount = 1, .dwPropertySize = 4 + 4 + 2 + 4 + sizeof(MS3DPRINT_CONFIG) + sizeof(MS3DPRINT_CONFIG_DATA), .dwPropertyDataType = 7, // (1=REG_SZ, 4=REG_DWORD, 7=REG_MULTI_SZ) .wPropertyNameLength = sizeof(MS3DPRINT_CONFIG), .PropertyName = MS3DPRINT_CONFIG, .dwPropertyDataLength = sizeof(MS3DPRINT_CONFIG_DATA), .PropertyData = MS3DPRINT_CONFIG_DATA }; /************************************************************************************************** ** WCID configuration information ** Hooked into UDC via UDC_GET_EXTRA_STRING #define. */ bool usb_task_extra_string(void) { static uint8_t udi_msft_magic[] = "MSFT100\xEE"; static uint8_t udi_cdc_name[] = "CDC interface"; #if ENABLED(SDSUPPORT) static uint8_t udi_msc_name[] = "MSC interface"; #endif struct extra_strings_desc_t { usb_str_desc_t header; #if ENABLED(SDSUPPORT) le16_t string[Max(Max(sizeof(udi_cdc_name) - 1, sizeof(udi_msc_name) - 1), sizeof(udi_msft_magic) - 1)]; #else le16_t string[Max(sizeof(udi_cdc_name) - 1, sizeof(udi_msft_magic) - 1)]; #endif }; static UDC_DESC_STORAGE struct extra_strings_desc_t extra_strings_desc = { .header.bDescriptorType = USB_DT_STRING }; uint8_t *str; uint8_t str_lgt = 0; // Link payload pointer to the string corresponding at request switch (udd_g_ctrlreq.req.wValue & 0xFF) { case UDI_CDC_IAD_STRING_ID: str_lgt = sizeof(udi_cdc_name) - 1; str = udi_cdc_name; break; #if ENABLED(SDSUPPORT) case UDI_MSC_STRING_ID: str_lgt = sizeof(udi_msc_name) - 1; str = udi_msc_name; break; #endif case 0xEE: str_lgt = sizeof(udi_msft_magic) - 1; str = udi_msft_magic; break; default: return false; } for (uint8_t i = 0; i < str_lgt; i++) extra_strings_desc.string[i] = cpu_to_le16((le16_t)str[i]); extra_strings_desc.header.bLength = 2 + str_lgt * 2; udd_g_ctrlreq.payload_size = extra_strings_desc.header.bLength; udd_g_ctrlreq.payload = (uint8_t*)&extra_strings_desc; // if the string is larger than request length, then cut it if (udd_g_ctrlreq.payload_size > udd_g_ctrlreq.req.wLength) { udd_g_ctrlreq.payload_size = udd_g_ctrlreq.req.wLength; } return true; } /************************************************************************************************** ** Handle device requests that the ASF stack doesn't */ bool usb_task_other_requests(void) { uint8_t *ptr = 0; uint16_t size = 0; if (Udd_setup_type() == USB_REQ_TYPE_VENDOR) { //if (udd_g_ctrlreq.req.bRequest == 0x30) if (1) { if (udd_g_ctrlreq.req.wIndex == 0x04) { ptr = (uint8_t*)µsoft_compatible_id_descriptor; size = (udd_g_ctrlreq.req.wLength); if (size > microsoft_compatible_id_descriptor.dwLength) size = microsoft_compatible_id_descriptor.dwLength; } else if (udd_g_ctrlreq.req.wIndex == 0x05) { ptr = (uint8_t*)µsoft_extended_properties_descriptor; size = (udd_g_ctrlreq.req.wLength); if (size > microsoft_extended_properties_descriptor.dwLength) size = microsoft_extended_properties_descriptor.dwLength; } else return false; } } udd_g_ctrlreq.payload_size = size; if (size == 0) { udd_g_ctrlreq.callback = 0; udd_g_ctrlreq.over_under_run = 0; } else udd_g_ctrlreq.payload = ptr; return true; } void usb_task_init(void) { uint16_t *ptr; // Disable USB peripheral so we start clean and avoid lockups otg_disable(); udd_disable(); // Set the USB interrupt to our stack UDD_SetStack(&USBD_ISR); // Start USB stack to authorize VBus monitoring udc_start(); // Patch in filament diameter - Be careful: String is in UNICODE (2bytes per char) ptr = µsoft_extended_properties_descriptor.PropertyData[0]; while (ptr[0] || ptr[1]) { // Double 0 flags end of resource // Found the filamentdiameter= unicode string if (ptr[0] == 'r' && ptr[1] == '=') { char diam[16]; char *sptr; // Patch in the filament diameter itoa((int)((DEFAULT_NOMINAL_FILAMENT_DIA) * 1000), diam, 10); // And copy it to the proper place, expanding it to unicode sptr = &diam[0]; ptr += 2; while (*sptr) *ptr++ = *sptr++; // Done! break; } // Go to the next character ptr++; } } #endif // ARDUINO_ARCH_SAM