From 84e887003d78268e10b58568cf623c3b24e14b7e Mon Sep 17 00:00:00 2001 From: Thibault RICHARD Date: Fri, 4 May 2012 18:58:24 +0200 Subject: [PATCH] [SAM] CDC working. Need to check inf file. --- hardware/arduino/sam/cores/sam/USB/CDC.cpp | 63 +++++-- hardware/arduino/sam/cores/sam/USB/HID.cpp | 10 +- hardware/arduino/sam/cores/sam/USB/USBAPI.h | 2 +- .../arduino/sam/cores/sam/USB/USBCore.cpp | 154 ++++++++-------- hardware/arduino/sam/cores/sam/USB/USBCore.h | 2 + hardware/arduino/sam/cores/sam/USB/USBDesc.h | 4 +- .../arduino/sam/cores/sam/arduino_due_cdc.inf | 102 +++++++++++ .../sam/cores/sam/libsam_sam3x8e_gcc_dbg.a | Bin 244550 -> 244918 bytes .../build_gcc/test_usb_device.mk | 4 +- .../validation_usb_device/test_usb_device.cpp | 41 ++++- .../sam/system/libsam/include/USB_device.h | 2 +- .../sam/system/libsam/include/uotghs.h | 3 +- .../arduino/sam/system/libsam/source/uotghs.c | 167 +++++++----------- .../arduino_due_x/libsam_sam3x8e_gcc_rel.a | Bin 58042 -> 75318 bytes .../libsam_sam3x8e_gcc_rel.a.txt | 151 ++++++++++++---- 15 files changed, 450 insertions(+), 255 deletions(-) create mode 100644 hardware/arduino/sam/cores/sam/arduino_due_cdc.inf diff --git a/hardware/arduino/sam/cores/sam/USB/CDC.cpp b/hardware/arduino/sam/cores/sam/USB/CDC.cpp index e79f7352f..bee073d2d 100644 --- a/hardware/arduino/sam/cores/sam/USB/CDC.cpp +++ b/hardware/arduino/sam/cores/sam/USB/CDC.cpp @@ -21,7 +21,12 @@ #ifdef CDC_ENABLED -#define CDC_SERIAL_BUFFER_SIZE 64 +#define CDC_SERIAL_BUFFER_SIZE 64 + +#define CDC_LINESTATE_DTR 0x01 // Data Terminal Ready +#define CDC_LINESTATE_RTS 0x02 // Ready to Send + +#define CDC_LINESTATE_READY (CDC_LINESTATE_RTS | CDC_LINESTATE_DTR) struct ring_buffer { @@ -35,17 +40,20 @@ ring_buffer cdc_rx_buffer = { { 0 }, 0, 0}; typedef struct { uint32_t dwDTERate; - uint8_t bCharFormat; + uint8_t bCharFormat; uint8_t bParityType; uint8_t bDataBits; - uint8_t lineState; + uint8_t lineState; } LineInfo; static volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 }; +_Pragma("pack(1)") static const CDCDescriptor _cdcInterface = { +#ifdef HID_ENABLED D_IAD(0,2,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,1), +#endif // CDC communication interface D_INTERFACE(CDC_ACM_INTERFACE,1,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,0), @@ -60,6 +68,7 @@ static const CDCDescriptor _cdcInterface = D_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT),USB_ENDPOINT_TYPE_BULK,0x40,0), D_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ),USB_ENDPOINT_TYPE_BULK,0x40,0) }; +_Pragma("pack()") int WEAK CDC_GetInterface(uint8_t* interfaceNum) { @@ -92,22 +101,22 @@ bool WEAK CDC_Setup(Setup& setup) 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 - if (1200 == _usbLineInfo.dwDTERate) { + if (1200 == _usbLineInfo.dwDTERate) + { // We check DTR state to determine if host port is open (bit 0 of lineState). if ((_usbLineInfo.lineState & 0x01) == 0) - { + { /* TODO, AVR Stuff *(uint16_t *)0x0800 = 0x7777; wdt_enable(WDTO_120MS); */ } - else - { + else + { // 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 250ms and eventually @@ -127,7 +136,7 @@ bool WEAK CDC_Setup(Setup& setup) int _serialPeek = -1; -void Serial_::begin(uint16_t baud_count) +void Serial_::begin(uint32_t baud_count) { } @@ -160,9 +169,13 @@ int Serial_::available(void) int Serial_::peek(void) { ring_buffer *buffer = &cdc_rx_buffer; - if (buffer->head == buffer->tail) { + + if (buffer->head == buffer->tail) + { return -1; - } else { + } + else + { return buffer->buffer[buffer->tail]; } } @@ -170,10 +183,14 @@ int Serial_::peek(void) int Serial_::read(void) { ring_buffer *buffer = &cdc_rx_buffer; + // if the head isn't ahead of the tail, we don't have any characters - if (buffer->head == buffer->tail) { + if (buffer->head == buffer->tail) + { return -1; - } else { + } + else + { unsigned char c = buffer->buffer[buffer->tail]; buffer->tail = (unsigned int)(buffer->tail + 1) % SERIAL_BUFFER_SIZE; return c; @@ -196,11 +213,16 @@ size_t Serial_::write(uint8_t c) // TODO - ZE - check behavior on different OSes and test what happens if an // open connection isn't broken cleanly (cable is yanked out, host dies // or locks up, or host virtual serial port hangs) - if (_usbLineInfo.lineState > 0) { + if (_usbLineInfo.lineState == CDC_LINESTATE_READY) + { int r = USBD_Send(CDC_TX,&c,1); - if (r > 0) { + + if (r > 0) + { + USBD_Flush(CDC_TX); return r; - } else { + } else + { setWriteError(); return 0; } @@ -216,10 +238,15 @@ size_t Serial_::write(uint8_t c) // actually ready to receive and display the data. // We add a short delay before returning to fix a bug observed by Federico // where the port is configured (lineState != 0) but not quite opened. -Serial_::operator bool() { +Serial_::operator bool() +{ bool result = false; - if (_usbLineInfo.lineState > 0) + + if (_usbLineInfo.lineState == CDC_LINESTATE_READY) + { result = true; + } + delay(10); return result; } diff --git a/hardware/arduino/sam/cores/sam/USB/HID.cpp b/hardware/arduino/sam/cores/sam/USB/HID.cpp index 50c417b9f..a02dbec8c 100644 --- a/hardware/arduino/sam/cores/sam/USB/HID.cpp +++ b/hardware/arduino/sam/cores/sam/USB/HID.cpp @@ -162,11 +162,8 @@ bool WEAK HID_Setup(Setup& setup) uint8_t r = setup.bRequest; uint8_t requestType = setup.bmRequestType; - printf("=> HID_Setup\r\n"); - if (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType) { - printf("=> REQUEST_DEVICETOHOST_CLASS_INTERFACE\r\n"); if (HID_GET_REPORT == r) { //HID_GetReport(); @@ -181,7 +178,6 @@ bool WEAK HID_Setup(Setup& setup) if (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType) { - printf("=> REQUEST_HOSTTODEVICE_CLASS_INTERFACE\r\n"); if (HID_SET_PROTOCOL == r) { _hid_protocol = setup.wValueL; @@ -289,9 +285,9 @@ extern const uint8_t _asciimap[128] = 0x00, // ENQ 0x00, // ACK 0x00, // BEL - 0x2a, // BS Backspace - 0x2b, // TAB Tab - 0x28, // LF Enter + 0x2a, // BS Backspace + 0x2b, // TAB Tab + 0x28, // LF Enter 0x00, // VT 0x00, // FF 0x00, // CR diff --git a/hardware/arduino/sam/cores/sam/USB/USBAPI.h b/hardware/arduino/sam/cores/sam/USB/USBAPI.h index 078ac0fe6..b65d20bb8 100644 --- a/hardware/arduino/sam/cores/sam/USB/USBAPI.h +++ b/hardware/arduino/sam/cores/sam/USB/USBAPI.h @@ -48,7 +48,7 @@ class Serial_ : public Stream private: RingBuffer *_cdc_rx_buffer; public: - void begin(uint16_t baud_count); + void begin(uint32_t baud_count); void end(void); virtual int available(void); diff --git a/hardware/arduino/sam/cores/sam/USB/USBCore.cpp b/hardware/arduino/sam/cores/sam/USB/USBCore.cpp index 44b5926de..711ff8a5d 100644 --- a/hardware/arduino/sam/cores/sam/USB/USBCore.cpp +++ b/hardware/arduino/sam/cores/sam/USB/USBCore.cpp @@ -18,6 +18,9 @@ #include "USBAPI.h" #include +//#define TRACE_CORE(x) x +#define TRACE_CORE(x) + static const uint32_t EndPoints[] = { EP_TYPE_CONTROL, @@ -76,12 +79,22 @@ const uint16_t STRING_IMANUFACTURER[12] = { #define DEVICE_CLASS 0x00 #endif +#if defined CDC_ENABLED && defined HID_ENABLED +#define USB_PID_FINAL (USB_PID|0x1001UL) +#elif defined HID_ENABLED +#define USB_PID_FINAL (USB_PID|0x1000UL) +#elif defined CDC_ENABLED +#define USB_PID_FINAL (USB_PID|0x0001UL) +#endif + // DEVICE DESCRIPTOR const DeviceDescriptor USB_DeviceDescriptor = - D_DEVICE(0x00,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1); + D_DEVICE(0x00,0x00,0x00,64,USB_VID,USB_PID_FINAL,0x100,IMANUFACTURER,IPRODUCT,0,1); const DeviceDescriptor USB_DeviceDescriptorA = - D_DEVICE(DEVICE_CLASS,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1); + D_DEVICE(DEVICE_CLASS,0x00,0x00,64,USB_VID,USB_PID_FINAL,0x100,IMANUFACTURER,IPRODUCT,0,1); + + //================================================================== //================================================================== @@ -90,7 +103,6 @@ volatile uint32_t _usbConfiguration = 0; volatile uint32_t _usbInitialized = 0; uint32_t _cdcComposite = 0; - //================================================================== //================================================================== @@ -101,7 +113,6 @@ class LockEP public: LockEP(uint32_t ep) : flags(cpu_irq_save()) { - //UDD_SetEP(ep & 0xF); } ~LockEP() { @@ -130,7 +141,7 @@ uint32_t USBD_Recv(uint32_t ep, void* d, uint32_t len) uint8_t* dst = (uint8_t*)d; while (n--) *dst++ = UDD_Recv8(ep & 0xF); - if (len && !UDD_FifoByteCount(ep & 0xF)) // release empty buffer + if (len && !UDD_FifoByteCount(ep & 0xF)) // release empty buffer UDD_ReleaseRX(ep & 0xF); return len; @@ -163,7 +174,7 @@ uint32_t USBD_Send(uint32_t ep, const void* d, uint32_t len) int r = len; const uint8_t* data = (const uint8_t*)d; - uint8_t timeout = 250; // 250ms timeout on send? TODO + uint8_t timeout = 250; // 250ms timeout on send? TODO while (len) { @@ -202,30 +213,27 @@ void USBD_InitControl(int end) // Clipped by _cmark/_cend int USBD_SendControl(uint8_t flags, const void* d, uint32_t len) { - int sent = len; - uint32_t i = 0; const uint8_t* data = (const uint8_t*)d; + uint32_t length = len; + uint32_t sent = 0; + uint32_t pos = 0; - printf("=> USBD_SendControl TOTAL len=%d\r\n", len); + TRACE_CORE(printf("=> USBD_SendControl TOTAL len=%d\r\n", len);) - for (i = 0; len > 64; ++i, len -= 64, _cmark += 64) + if (_cmark < _cend) { - if (_cmark < _cend) + while (len > 0) { - UDD_Send(EP0, data + (i * 64), 64); - UDD_ClearIN(); // Fifo is full, release this packet - UDD_WaitIN(); // Wait for new FIFO buffer to be ready + sent = UDD_Send(EP0, data + pos, len); + TRACE_CORE(printf("=> USBD_SendControl sent=%d\r\n", sent);) + pos += sent; + len -= sent; } } - if (len > 0) - { - if (_cmark < _cend) - UDD_Send(EP0, data + (i * 64), len); - _cmark += len; - } + _cmark += length; - return sent; + return length; } // Does not timeout or cross fifo boundaries @@ -234,7 +242,7 @@ int USBD_SendControl(uint8_t flags, const void* d, uint32_t len) int USBD_RecvControl(void* d, uint32_t len) { UDD_WaitOUT() ; - UDD_Recv(EP0, (uint8_t*)d, len ) ; // WILL NOT WORK WITH CDC + UDD_Recv(EP0, (uint8_t*)d, len ) ; UDD_ClearOUT() ; return len ; @@ -245,7 +253,7 @@ bool USBD_ClassInterfaceRequest(Setup& setup) { uint8_t i = setup.wIndex; - printf("=> USBD_ClassInterfaceRequest\r\n"); + TRACE_CORE(printf("=> USBD_ClassInterfaceRequest\r\n");) #ifdef CDC_ENABLED if ( CDC_ACM_INTERFACE == i ) @@ -277,7 +285,8 @@ int USBD_SendInterfaces(void) total += HID_GetInterface(&interfaces) ; #endif - printf("=> USBD_SendInterfaces, total=%d interfaces=%d\r\n", total, interfaces); + total = total; // Get rid of compiler warning + TRACE_CORE(printf("=> USBD_SendInterfaces, total=%d interfaces=%d\r\n", total, interfaces);) return interfaces; } @@ -288,17 +297,17 @@ static bool USBD_SendConfiguration(int maxlen) { // Count and measure interfaces USBD_InitControl(0); - printf("=> USBD_SendConfiguration _cmark1=%d\r\n", _cmark); + //TRACE_CORE(printf("=> USBD_SendConfiguration _cmark1=%d\r\n", _cmark);) int interfaces = USBD_SendInterfaces(); - printf("=> USBD_SendConfiguration _cmark2=%d\r\n", _cmark); - printf("=> USBD_SendConfiguration sizeof=%d\r\n", sizeof(ConfigDescriptor)); + //TRACE_CORE(printf("=> USBD_SendConfiguration _cmark2=%d\r\n", _cmark);) + //TRACE_CORE(printf("=> USBD_SendConfiguration sizeof=%d\r\n", sizeof(ConfigDescriptor));) _Pragma("pack(1)") ConfigDescriptor config = D_CONFIG(_cmark + sizeof(ConfigDescriptor),interfaces); _Pragma("pack()") - printf("=> USBD_SendConfiguration clen=%d\r\n", config.clen); + //TRACE_CORE(printf("=> USBD_SendConfiguration clen=%d\r\n", config.clen);) -printf("=> USBD_SendConfiguration maxlen=%d\r\n", maxlen); + //TRACE_CORE(printf("=> USBD_SendConfiguration maxlen=%d\r\n", maxlen);) // Now send them USBD_InitControl(maxlen); @@ -315,7 +324,7 @@ static bool USBD_SendDescriptor(Setup& setup) if ( USB_CONFIGURATION_DESCRIPTOR_TYPE == t ) { - printf("=> USBD_SendDescriptor : USB_CONFIGURATION_DESCRIPTOR_TYPE length=%d\r\n", setup.wLength); + TRACE_CORE(printf("=> USBD_SendDescriptor : USB_CONFIGURATION_DESCRIPTOR_TYPE length=%d\r\n", setup.wLength);) return USBD_SendConfiguration(setup.wLength); } @@ -323,14 +332,14 @@ static bool USBD_SendDescriptor(Setup& setup) #ifdef HID_ENABLED if ( HID_REPORT_DESCRIPTOR_TYPE == t ) { - puts("=> USBD_SendDescriptor : HID_REPORT_DESCRIPTOR_TYPE\r\n"); + TRACE_CORE(puts("=> USBD_SendDescriptor : HID_REPORT_DESCRIPTOR_TYPE\r\n");) return HID_GetDescriptor( t ) ; } #endif if (USB_DEVICE_DESCRIPTOR_TYPE == t) { - puts("=> USBD_SendDescriptor : USB_DEVICE_DESCRIPTOR_TYPE\r\n"); + TRACE_CORE(puts("=> USBD_SendDescriptor : USB_DEVICE_DESCRIPTOR_TYPE\r\n");) if ( setup.wLength == 8 ) { _cdcComposite = 1; @@ -339,7 +348,7 @@ static bool USBD_SendDescriptor(Setup& setup) } else if (USB_STRING_DESCRIPTOR_TYPE == t) { - puts("=> USBD_SendDescriptor : USB_STRING_DESCRIPTOR_TYPE\r\n"); + TRACE_CORE(puts("=> USBD_SendDescriptor : USB_STRING_DESCRIPTOR_TYPE\r\n");) if (setup.wValueL == 0) desc_addr = (const uint8_t*)&STRING_LANGUAGE; else if (setup.wValueL == IPRODUCT) @@ -360,7 +369,7 @@ static bool USBD_SendDescriptor(Setup& setup) desc_length = *desc_addr; } - printf("=> USBD_SendDescriptor : desc_addr=%x desc_length=%d\r\n", desc_addr, desc_length); + TRACE_CORE(printf("=> USBD_SendDescriptor : desc_addr=%x desc_length=%d\r\n", desc_addr, desc_length);) USBD_SendControl(0, desc_addr, desc_length); return true; @@ -372,46 +381,37 @@ static void USB_ISR(void) // End of Reset if (Is_udd_reset()) { - printf(">>> End of Reset\r\n"); + TRACE_CORE(printf(">>> End of Reset\r\n");) + // Reset USB address to 0 udd_configure_address(0); udd_enable_address(); // Configure EP 0 - //UDD_SetEP(0); UDD_InitEP(0, EP_TYPE_CONTROL); udd_enable_setup_received_interrupt(0); udd_enable_endpoint_interrupt(0); _usbConfiguration = 0; - udd_ack_reset(); /* /!\/!\/!\ TAKEN FROM ASF TO CLEAR ISR /!\/!\/!\ */ + udd_ack_reset(); } - // Start of Frame - happens every millisecond so we use it for TX and RX LED one-shot timing, too - if (Is_udd_sof()) - { - //printf(">>> Start of Frame\r\n"); #ifdef CDC_ENABLED - USBD_Flush(CDC_TX); // Send a tx frame if found - while (USBD_Available(CDC_RX)) // Handle received bytes (if any) + if (Is_udd_endpoint_interrupt(CDC_RX)) + { + udd_ack_out_received(CDC_RX); + + // Handle received bytes + while (USBD_Available(CDC_RX)) Serial.accept(); + + udd_ack_fifocon(CDC_RX) ; + } #endif - // check whether the one-shot period has elapsed. if so, turn off the LED - /*if (TxLEDPulse && !(--TxLEDPulse)) - TXLED0; - if (RxLEDPulse && !(--RxLEDPulse)) - RXLED0;*/ - - udd_ack_sof(); /* /!\/!\/!\ TAKEN FROM ASF TO CLEAR ISR /!\/!\/!\ */ - } - // EP 0 Interrupt if (Is_udd_endpoint_interrupt(0)) { - //UDD_SetEP(0); - - //printf(">>> EP0 Int: 0x%x\r\n", UOTGHS->UOTGHS_DEVEPTISR[0]); if ( !UDD_ReceivedSetupInt() ) { @@ -422,17 +422,15 @@ static void USB_ISR(void) UDD_Recv(EP0, (uint8_t*)&setup, 8); UDD_ClearSetupInt(); - //printf(">>> EP0 Int: AP clear: 0x%x\r\n", UOTGHS->UOTGHS_DEVEPTISR[0]); - uint8_t requestType = setup.bmRequestType; if (requestType & REQUEST_DEVICETOHOST) { - printf(">>> EP0 Int: IN Request\r\n"); + TRACE_CORE(printf(">>> EP0 Int: IN Request\r\n");) UDD_WaitIN(); } else { - printf(">>> EP0 Int: OUT Request\r\n"); + TRACE_CORE(printf(">>> EP0 Int: OUT Request\r\n");) UDD_ClearIN(); } @@ -441,12 +439,12 @@ static void USB_ISR(void) bool ok = true ; if (REQUEST_STANDARD == (requestType & REQUEST_TYPE)) { - // Standard Requests + // Standard Requests uint8_t r = setup.bRequest; if (GET_STATUS == r) { - puts(">>> EP0 Int: GET_STATUS\r\n"); - UDD_Send8(EP0, 0); // TODO + TRACE_CORE(puts(">>> EP0 Int: GET_STATUS\r\n");) + UDD_Send8(EP0, 0); // TODO UDD_Send8(EP0, 0); } else if (CLEAR_FEATURE == r) @@ -457,65 +455,70 @@ static void USB_ISR(void) } else if (SET_ADDRESS == r) { - puts(">>> EP0 Int: SET_ADDRESS\r\n"); + TRACE_CORE(puts(">>> EP0 Int: SET_ADDRESS\r\n");) UDD_WaitIN(); UDD_SetAddress(setup.wValueL); } else if (GET_DESCRIPTOR == r) { - puts(">>> EP0 Int: GET_DESCRIPTOR\r\n"); + TRACE_CORE(puts(">>> EP0 Int: GET_DESCRIPTOR\r\n");) ok = USBD_SendDescriptor(setup); } else if (SET_DESCRIPTOR == r) { - puts(">>> EP0 Int: SET_DESCRIPTOR\r\n"); + TRACE_CORE(puts(">>> EP0 Int: SET_DESCRIPTOR\r\n");) ok = false; } else if (GET_CONFIGURATION == r) { - puts(">>> EP0 Int: GET_CONFIGURATION\r\n"); + TRACE_CORE(puts(">>> EP0 Int: GET_CONFIGURATION\r\n");) UDD_Send8(EP0, 1); } else if (SET_CONFIGURATION == r) { if (REQUEST_DEVICE == (requestType & REQUEST_RECIPIENT)) { - printf(">>> EP0 Int: SET_CONFIGURATION REQUEST_DEVICE %d\r\n", setup.wValueL); + TRACE_CORE(printf(">>> EP0 Int: SET_CONFIGURATION REQUEST_DEVICE %d\r\n", setup.wValueL);) UDD_InitEndpoints(EndPoints, (sizeof(EndPoints) / sizeof(EndPoints[0]))); _usbConfiguration = setup.wValueL; + +#ifdef CDC_ENABLED + // Enable interrupt for CDC reception from host (OUT packet) + udd_enable_out_received_interrupt(CDC_RX); + udd_enable_endpoint_interrupt(CDC_RX); +#endif } else { - puts(">>> EP0 Int: SET_CONFIGURATION failed!\r\n"); + TRACE_CORE(puts(">>> EP0 Int: SET_CONFIGURATION failed!\r\n");) ok = false; } } else if (GET_INTERFACE == r) { - puts(">>> EP0 Int: GET_INTERFACE\r\n"); + TRACE_CORE(puts(">>> EP0 Int: GET_INTERFACE\r\n");) } else if (SET_INTERFACE == r) { - puts(">>> EP0 Int: SET_INTERFACE\r\n"); + TRACE_CORE(puts(">>> EP0 Int: SET_INTERFACE\r\n");) } } else { - puts(">>> EP0 Int: ClassInterfaceRequest\r\n"); - USBD_InitControl(setup.wLength); // Max length of transfer + TRACE_CORE(puts(">>> EP0 Int: ClassInterfaceRequest\r\n");) + USBD_InitControl(setup.wLength); // Max length of transfer ok = USBD_ClassInterfaceRequest(setup); } if (ok) { - puts(">>> EP0 Int: Send packet\r\n"); - UDD_ClearOUT(); // rajouté par moi, pe pas nécessaire car la fifo est suffisament grande + TRACE_CORE(puts(">>> EP0 Int: Send packet\r\n");) UDD_ClearIN(); } else { - puts(">>> EP0 Int: Stall\r\n"); + TRACE_CORE(puts(">>> EP0 Int: Stall\r\n");) UDD_Stall(); } } @@ -523,7 +526,6 @@ static void USB_ISR(void) void USBD_Flush(uint32_t ep) { - //UDD_SetEP(ep); if (UDD_FifoByteCount(ep)) UDD_ReleaseTX(ep); } diff --git a/hardware/arduino/sam/cores/sam/USB/USBCore.h b/hardware/arduino/sam/cores/sam/USB/USBCore.h index 68b18ff65..c21398f77 100644 --- a/hardware/arduino/sam/cores/sam/USB/USBCore.h +++ b/hardware/arduino/sam/cores/sam/USB/USBCore.h @@ -234,8 +234,10 @@ typedef struct typedef struct { +#ifdef HID_ENABLED // IAD IADDescriptor iad; // Only needed on compound device +#endif // Control InterfaceDescriptor cif; // diff --git a/hardware/arduino/sam/cores/sam/USB/USBDesc.h b/hardware/arduino/sam/cores/sam/USB/USBDesc.h index db912c9ba..340ebcca5 100644 --- a/hardware/arduino/sam/cores/sam/USB/USBDesc.h +++ b/hardware/arduino/sam/cores/sam/USB/USBDesc.h @@ -16,8 +16,8 @@ ** SOFTWARE. */ -//#define CDC_ENABLED -#define HID_ENABLED +#define CDC_ENABLED +//#define HID_ENABLED #ifdef CDC_ENABLED diff --git a/hardware/arduino/sam/cores/sam/arduino_due_cdc.inf b/hardware/arduino/sam/cores/sam/arduino_due_cdc.inf new file mode 100644 index 000000000..8d91b7c87 --- /dev/null +++ b/hardware/arduino/sam/cores/sam/arduino_due_cdc.inf @@ -0,0 +1,102 @@ +; +; Windows USB CDC Driver Setup File for ARDUINO Due +; +; On Windows 7, right click to update driver software. It may take a while to +; get this option, even if you cancel the auto driver search. +; choose "browse my computer for driver software", +; choose "let me pick from a list of device drivers on my computer", +; Click "have disk" and browse to this .inf file +; If there is a problem, right click and uninstall, checking delete driver software. + +;------------------------------------------------------------------------------ + +[Version] +Signature="$Windows NT$" +Class=Ports +ClassGuid={4D36E978-E325-11CE-BFC1-08002BE10318} +Provider=%ARDUINO% +LayoutFile=layout.inf + +DriverVer= 03/09/2011,2.0.0.0 + +[Manufacturer] +%ARDUINO%=DeviceList,NTamd64 + +[DestinationDirs] +DefaultDestDir=12 + + +;------------------------------------------------------------------------------ +; Windows 2000/XP/Vista32 Support +;------------------------------------------------------------------------------ +[DriverInstall.nt] +include=mdmcpq.inf +CopyFiles=DriverCopyFiles.nt +AddReg=DriverInstall.nt.AddReg + +[DriverCopyFiles.nt] +usbser.sys,,,0x20 + +[DriverInstall.nt.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[DriverInstall.nt.Services] +AddService=usbser, 0x00000002, DriverService.nt + +[DriverService.nt] +DisplayName=%USBtoSerialConverter% +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\usbser.sys + +;------------------------------------------------------------------------------ +; Windows Vista64 Support +;------------------------------------------------------------------------------ + +[DriverInstall.NTamd64] +include=mdmcpq.inf +CopyFiles=DriverCopyFiles.NTamd64 +AddReg=DriverInstall.NTamd64.AddReg + +[DriverCopyFiles.NTamd64] +usbser.sys,,,0x20 + +[DriverInstall.NTamd64.AddReg] +HKR,,DevLoader,,*ntkern +HKR,,NTMPDriver,,usbser.sys +HKR,,EnumPropPages32,,"MsPorts.dll,SerialPortPropPageProvider" + +[DriverInstall.NTamd64.Services] +AddService=usbser, 0x00000002, DriverService.NTamd64 + +[DriverService.NTamd64] +DisplayName=%USBtoSerialConverter% +ServiceType=1 +StartType=3 +ErrorControl=1 +ServiceBinary=%12%\usbser.sys + + +;------------------------------------------------------------------------------ +; VID/PID Settings +;------------------------------------------------------------------------------ +[SourceDisksFiles] +[SourceDisksNames] +[DeviceList] +%USBtoSerialConverter%=DriverInstall, USB\VID_2341&PID_CAFF ; CDC +%USBtoSerialConverter%=DriverInstall, USB\VID_2341&PID_DAFF&MI_00 ; HID + CDC + +[DeviceList.NTamd64] +%USBtoSerialConverter%=DriverInstall, USB\VID_2341&PID_CAFF ; CDC +%USBtoSerialConverter%=DriverInstall, USB\VID_2341&PID_DAFF&MI_00 ; HID + CDC + +;------------------------------------------------------------------------------ +; String Definitions +;------------------------------------------------------------------------------ +[Strings] + +ARDUINO="ARDUINO LLC" ; String value for the ARDUINO symbol +USBtoSerialConverter="Arduino USB to Serial Converter" ; String value for the USBtoSerialConverter symbol diff --git a/hardware/arduino/sam/cores/sam/libsam_sam3x8e_gcc_dbg.a b/hardware/arduino/sam/cores/sam/libsam_sam3x8e_gcc_dbg.a index e3a232488373b0122bd6ce977b5fe1a507a23d7e..49dec1323d75263eaec5deec4bbbaad515469682 100644 GIT binary patch delta 4425 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 a&u{C4a~10_sc86f`X@2{hP_oW+WrHOVnQMS delta 3985 zcmchae^6A{701uLci*z$N>^D|5Rm1Euz^JuHvEWaz@SAG<(Fv(YaWtdN33M?xlVCKpGhv*Nl45*jForgnKocfor|FDNXuwXY*3mSr)>@L9p7ZuD$Y$yv z{i8ec_H)nqo*(z!clW)wr~eiC{_V()II|}=-|O+dk<=i3?nNN}*JHy@5TQr=X%P1w z{!|e6IehmM{}0-O_nqB;nLz&;)2C6mhh%k#F?WXJ2P4Vw6X()A)B1gL3h9 zF<1Y6D%azKa>p{hDJ>{ddp)X!Kes&KDG1a%_XhtWY?|kTxA-mcyq3xo@xIO8#qM6K&p{#w)>Jju= zghv4iDePbv3Kll0#G%rF$^!NjozYBY6q<+L4)mt7Z&PP`z}tb|vp#f>M*^fZwvs>{ zcxOY*d^#69BZYU?rL{Eva7tb8%A+a{cEwQJUF&Fh=CZ#VX6pk#04-V-Y5|AoGP=_p zrW**&gXJA2`c7rv!f!3f7nsX>G;ZNkiqLCJB`H`TFqI@mNxbukK(iFptQ|5~fd%WN zGa~?FfJ(V>`acbpN)MI_n{mZ?g&;hfSjSMyFSPXk7L6|Cc(zB*o2N3Bj! zRjZRb*~M6$JY^cIlOLQAUMAlFZPcPbOWFn9*fN71rR!QcbzSpN8AheZrq-ua)o90A z-^GQ~$^#`TF%a;2GzSCA?+H9ad!lwKus{PJbOs}9CnGah3oUB%RDW8SRxEoi7~DJ= zoXY+{{hth!U5SA`7=2sq*O|=8)ZEJi(4n z5etQtMRB}iq^DxD4B6VkqG!8NGAfis66Z49%feQ@o`Ni{FxW%&yDjo z5pbUVjA!dbpYa~fJ3wB#!q>@?E4({supxc%RbIqh32app*KkQp8fdsE03wBDZENCe znlD|RxW-F-W5AaF=o8>3W_Frid0WhBZ{ntDfrs&zGr+|(pP#XHl_xAw{*XpJjBOB! zEB|7N615#h(UoT5UKtWKM!&^CN8j;~%9p3KQ0a5VviEHZiNB5U-*A47k*dM*fsoTV z$XV9DNuM*YK!-V&4yoM5SyR>l`S^97yzu8Z-rsvOWJs@~qtkm?sXtNy0mLdxH09%z7kEJjL8cj)@^&UL~8ESi{#>$uq=VRno%6 znj< zyoTtmmM$bqkrkRq<0I8_2f0s$$>YS)F#lk!Lw>3W8?(#5Vu(B^gjFV)MI3Li$YL`F zS!-l7@sve&AhmJ43zf@X_lhX_kr@-+a?C8!v`V~XJ|ky@;bh`!q$dm`Kfy=bYw~;K zIuI}Spo@=wU-l6F$?`n$wFY^I_)>~=T9ChJlq-;$e*wUUvdx02`%%s&$TYtJf3aCHc!NfIkUNyU%dqvqIU*{O|^mRJR_j`%1* zJux3(lj3Hjw<>)H@gaa+ihGnUmHwvUdx{f^9G`W(5O2!nzyKnM*f^1hjXjD>6;~6N z08}YHO2qgUrT 0) { + // read incoming serial data: + //char inChar = Serial.read(); + // Type the next ASCII value from what you received: + // Keyboard.write(inChar+1); + //Serial.print("toto\r\n"); + // check for incoming serial data: + + if (Serial.available() > 0) { + // read incoming serial data: + char inChar = Serial.read(); + // Type the next ASCII value from what you received: + Serial.print(inChar); + Serial1.print(inChar); + } + delay(10); +} + + diff --git a/hardware/arduino/sam/system/libsam/include/USB_device.h b/hardware/arduino/sam/system/libsam/include/USB_device.h index a02ea07b5..816b8bf17 100644 --- a/hardware/arduino/sam/system/libsam/include/USB_device.h +++ b/hardware/arduino/sam/system/libsam/include/USB_device.h @@ -48,7 +48,7 @@ extern uint8_t UDD_GetConfiguration(void) ; -extern void UDD_Send(uint32_t ep, const void* data, uint32_t len); +extern uint32_t UDD_Send(uint32_t ep, const void* data, uint32_t len); extern void UDD_Send8(uint32_t ep, uint8_t data ); extern uint8_t UDD_Recv8(uint32_t ep); extern void UDD_Recv(uint32_t ep, uint8_t* data, uint32_t len); diff --git a/hardware/arduino/sam/system/libsam/include/uotghs.h b/hardware/arduino/sam/system/libsam/include/uotghs.h index a081aa65e..21b584977 100644 --- a/hardware/arduino/sam/system/libsam/include/uotghs.h +++ b/hardware/arduino/sam/system/libsam/include/uotghs.h @@ -21,8 +21,9 @@ #define MAX_ENDPOINTS 10 - #define EP0 0 +#define EP0_SIZE 64 +#define EPX_SIZE 1024 #define EP_SINGLE_64 (0x32UL) // EP0 #define EP_DOUBLE_64 (0x36UL) // Other endpoints diff --git a/hardware/arduino/sam/system/libsam/source/uotghs.c b/hardware/arduino/sam/system/libsam/source/uotghs.c index a6c46779e..276a69015 100644 --- a/hardware/arduino/sam/system/libsam/source/uotghs.c +++ b/hardware/arduino/sam/system/libsam/source/uotghs.c @@ -21,9 +21,10 @@ #if SAM3XA_SERIES -static void (*gpf_isr)(void) = (0UL); +//#define TRACE_UOTGHS(x) x +#define TRACE_UOTGHS(x) -//static volatile uint32_t ul_ep = (0UL); +static void (*gpf_isr)(void) = (0UL); static volatile uint32_t ul_send_fifo_ptr[MAX_ENDPOINTS]; static volatile uint32_t ul_recv_fifo_ptr[MAX_ENDPOINTS]; @@ -94,19 +95,12 @@ uint32_t UDD_Init(void) void UDD_Attach(void) { - //USBCON = ((1< UDD_Attach\r\n"); - + TRACE_UOTGHS(printf("=> UDD_Attach\r\n");) otg_unfreeze_clock(); - // This section of clock check can be improved with a chek of - // USB clock source via sysclk() // Check USB clock because the source can be a PLL while (!Is_otg_clock_usable()); @@ -115,52 +109,30 @@ void UDD_Attach(void) // Enable USB line events udd_enable_reset_interrupt(); - //udd_enable_suspend_interrupt(); - //udd_enable_wake_up_interrupt(); - - - //////////////udd_enable_sof_interrupt(); - - - - // Reset following interupts flag - //udd_ack_reset(); - //udd_ack_sof(); - - - // The first suspend interrupt must be forced - // The first suspend interrupt is not detected else raise it - //udd_raise_suspend(); - - //udd_ack_wake_up(); - //otg_freeze_clock(); + //udd_enable_sof_interrupt(); cpu_irq_restore(flags); } void UDD_Detach(void) { - //printf("=> UDD_Detach\r\n"); + TRACE_UOTGHS(printf("=> UDD_Detach\r\n");) UOTGHS->UOTGHS_DEVCTRL |= UOTGHS_DEVCTRL_DETACH; } void UDD_InitEP( uint32_t ul_ep_nb, uint32_t ul_ep_cfg ) { - ul_ep_nb = ul_ep_nb & 0xF; // EP range is 0..9, hence mask is 0xF. - //printf("=> UDD_InitEP : init EP %d\r\n", ul_ep_nb); - // Reset EP - //UOTGHS->UOTGHS_DEVEPT = (UOTGHS_DEVEPT_EPRST0 << ul_ep_nb); + TRACE_UOTGHS(printf("=> UDD_InitEP : init EP %d\r\n", ul_ep_nb);) + // Configure EP UOTGHS->UOTGHS_DEVEPTCFG[ul_ep_nb] = ul_ep_cfg; - // Allocate memory - //udd_allocate_memory(ul_ep_nb); // Enable EP -// UOTGHS->UOTGHS_DEVEPT |= (UOTGHS_DEVEPT_EPEN0 << ul_ep_nb); -udd_enable_endpoint(ul_ep_nb); + udd_enable_endpoint(ul_ep_nb); + if (!Is_udd_endpoint_configured(ul_ep_nb)) { - //printf("=> UDD_InitEP : ############################## ERROR FAILED TO INIT EP %d\r\n", ul_ep_nb); + TRACE_UOTGHS(printf("=> UDD_InitEP : ERROR FAILED TO INIT EP %d\r\n", ul_ep_nb);) } } @@ -169,67 +141,28 @@ void UDD_InitEndpoints(const uint32_t* eps_table, const uint32_t ul_eps_table_si { uint32_t ul_ep_nb ; - - - - for (ul_ep_nb = 1; ul_ep_nb < ul_eps_table_size; ul_ep_nb++) - - -/*void UDD_InitEndpoints(const uint32_t eps_table[]) -{ - uint32_t ul_ep_nb ; - - -//printf("=> UDD_InitEndpoints : Taille tableau %d %d\r\n", sizeof(eps_table), (sizeof(eps_table) / sizeof(eps_table[0]))); - - for (ul_ep_nb = 1; ul_ep_nb < sizeof(eps_table) / sizeof(eps_table[0]); ul_ep_nb++)*/ { - // Reset Endpoint Fifos - /* UOTGHS->UOTGHS_DEVEPTISR[ul_EP].UDPHS_EPTCLRSTA = UDPHS_EPTCLRSTA_TOGGLESQ | UDPHS_EPTCLRSTA_FRCESTALL; - UOTGHS->UOTGHS_DEVEPT = 1<UDPHS_EPT[ul_EP].UDPHS_EPTCFG = _initEndpoints[ul_EP]; - - while( (signed int)UDPHS_EPTCFG_EPT_MAPD != (signed int)((UOTGHS->UDPHS_EPT[ul_EP].UDPHS_EPTCFG) & (unsigned int)UDPHS_EPTCFG_EPT_MAPD) ) - ; - UOTGHS->UDPHS_EPT[ul_EP].UDPHS_EPTCTLENB = UDPHS_EPTCTLENB_EPT_ENABL; - - // UECFG1X = EP_DOUBLE_64; - }*/ - - //printf("=> UDD_InitEndpoints : init EP %d\r\n", ul_ep_nb); - - - // Reset EP - //UOTGHS->UOTGHS_DEVEPT = (UOTGHS_DEVEPT_EPRST0 << ul_ep_nb); // Configure EP UOTGHS->UOTGHS_DEVEPTCFG[ul_ep_nb] = eps_table[ul_ep_nb]; - // Allocate memory - //udd_allocate_memory(ul_ep_nb); // Enable EP - //UOTGHS->UOTGHS_DEVEPT |= (UOTGHS_DEVEPT_EPEN0 << ul_ep_nb); -udd_enable_endpoint(ul_ep_nb); + udd_enable_endpoint(ul_ep_nb); + if (!Is_udd_endpoint_configured(ul_ep_nb)) { - //printf("=> UDD_InitEP : ############################## ERROR FAILED TO INIT EP %d\r\n", ul_ep_nb); + TRACE_UOTGHS(printf("=> UDD_InitEP : ERROR FAILED TO INIT EP %d\r\n", ul_ep_nb);) } } - } // Wait until ready to accept IN packet. void UDD_WaitIN(void) { - //while (!(UEINTX & (1<UOTGHS_DEVEPTISR[EP0] & UOTGHS_DEVEPTISR_TXINI)) ; } void UDD_WaitOUT(void) { - //while (!(UEINTX & (1<UOTGHS_DEVEPTISR[EP0] & UOTGHS_DEVEPTISR_RXOUTI)) ; } @@ -237,15 +170,14 @@ void UDD_WaitOUT(void) // Send packet. void UDD_ClearIN(void) { - //printf("=> UDD_ClearIN: sent %d bytes\r\n", ul_send_index); - // UEINTX = ~(1< UDD_ClearIN: sent %d bytes\r\n", ul_send_fifo_ptr[EP0]);) + UOTGHS->UOTGHS_DEVEPTICR[EP0] = UOTGHS_DEVEPTICR_TXINIC; ul_send_fifo_ptr[EP0] = 0; } void UDD_ClearOUT(void) { - // UEINTX = ~(1<UOTGHS_DEVEPTICR[EP0] = UOTGHS_DEVEPTICR_RXOUTIC; ul_recv_fifo_ptr[EP0] = 0; } @@ -254,8 +186,6 @@ void UDD_ClearOUT(void) // Return true if new IN FIFO buffer available. uint32_t UDD_WaitForINOrOUT(void) { - //while (!(UEINTX & ((1<UOTGHS_DEVEPTISR[EP0] & (UOTGHS_DEVEPTISR_TXINI | UOTGHS_DEVEPTISR_RXOUTI))) ; return ((UOTGHS->UOTGHS_DEVEPTISR[EP0] & UOTGHS_DEVEPTISR_RXOUTI) == 0); @@ -268,29 +198,61 @@ uint32_t UDD_ReceivedSetupInt(void) void UDD_ClearSetupInt(void) { - //UEINTX = ~((1<UOTGHS_DEVEPTICR[ul_ep] = (UOTGHS_DEVEPTICR_RXSTPIC | UOTGHS_DEVEPTICR_RXOUTIC | UOTGHS_DEVEPTICR_TXINIC); UOTGHS->UOTGHS_DEVEPTICR[EP0] = (UOTGHS_DEVEPTICR_RXSTPIC); } -void UDD_Send(uint32_t ep, const void* data, uint32_t len) +uint32_t UDD_Send(uint32_t ep, const void* data, uint32_t len) { const uint8_t *ptr_src = data; uint8_t *ptr_dest = (uint8_t *) &udd_get_endpoint_fifo_access8(ep); uint32_t i; - //printf("=> UDD_Send : ep=%d ptr_dest=%d len=%d\r\n", ep, ul_send_fifo_ptr[ep], len); + TRACE_UOTGHS(printf("=> UDD_Send (1): ep=%d ul_send_fifo_ptr=%d len=%d\r\n", ep, ul_send_fifo_ptr[ep], len);) + + if (ep == EP0) + { + if (ul_send_fifo_ptr[ep] + len > EP0_SIZE) + len = EP0_SIZE - ul_send_fifo_ptr[ep]; + } + else + { + if (ul_send_fifo_ptr[ep] + len > EPX_SIZE) + len = EPX_SIZE - ul_send_fifo_ptr[ep]; + } for (i = 0, ptr_dest += ul_send_fifo_ptr[ep]; i < len; ++i) *ptr_dest++ = *ptr_src++; ul_send_fifo_ptr[ep] += i; + + + if (ep == EP0) + { + TRACE_UOTGHS(printf("=> UDD_Send (2): ep=%d ptr_dest=%d maxlen=%d\r\n", ep, ul_send_fifo_ptr[ep], EP0_SIZE);) + if (ul_send_fifo_ptr[ep] == EP0_SIZE) + { + UDD_ClearIN(); // Fifo is full, release this packet + UDD_WaitIN(); // Wait for new FIFO buffer to be ready + } + } + else + { + if (ul_send_fifo_ptr[ep] == EPX_SIZE) + { + UDD_ClearIN(); // Fifo is full, release this packet + UDD_WaitIN(); // Wait for new FIFO buffer to be ready + } + } + + return len; } void UDD_Send8(uint32_t ep, uint8_t data ) { uint8_t *ptr_dest = (uint8_t *) &udd_get_endpoint_fifo_access8(ep); - //printf("=> UDD_Send8 : ul_send_index=%d data=0x%x\r\n", ul_send_index, data); + + TRACE_UOTGHS(printf("=> UDD_Send8 : ul_send_fifo_ptr=%d data=0x%x\r\n", ul_send_fifo_ptr[ep], data);) + ptr_dest[ul_send_fifo_ptr[ep]] = data; ul_send_fifo_ptr[ep] += 1; } @@ -299,7 +261,9 @@ uint8_t UDD_Recv8(uint32_t ep) { uint8_t *ptr_dest = (uint8_t *) &udd_get_endpoint_fifo_access8(ep); uint8_t data = ptr_dest[ul_recv_fifo_ptr[ep]]; - ////printf("=> UDD_Recv8 : ul_recv_index=%d\r\n", ul_recv_index); + + TRACE_UOTGHS(printf("=> UDD_Recv8 : ul_recv_fifo_ptr=%d\r\n", ul_recv_fifo_ptr[ep]);) + ul_recv_fifo_ptr[ep] += 1; return data; } @@ -318,7 +282,6 @@ void UDD_Recv(uint32_t ep, uint8_t* data, uint32_t len) void UDD_Stall(void) { - //UECONX = (1<UOTGHS_DEVEPT = (UOTGHS_DEVEPT_EPEN0 << EP0); UOTGHS->UOTGHS_DEVEPTIER[EP0] = UOTGHS_DEVEPTIER_STALLRQS; } @@ -331,12 +294,7 @@ uint32_t UDD_FifoByteCount(uint32_t ep) void UDD_ReleaseRX(uint32_t ep) { -/* UEINTX = 0x6B; // FIFOCON=0 NAKINI=1 RWAL=1 NAKOUTI=0 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=1 - clear fifocon = send and switch bank - nakouti a clearer - rxouti/killbank a clearer*/ - - //puts("=> UDD_ReleaseRX\r\n"); + TRACE_UOTGHS(puts("=> UDD_ReleaseRX\r\n");) UOTGHS->UOTGHS_DEVEPTICR[ep] = (UOTGHS_DEVEPTICR_NAKOUTIC | UOTGHS_DEVEPTICR_RXOUTIC); UOTGHS->UOTGHS_DEVEPTIDR[ep] = UOTGHS_DEVEPTIDR_FIFOCONC; ul_recv_fifo_ptr[ep] = 0; @@ -344,13 +302,7 @@ void UDD_ReleaseRX(uint32_t ep) void UDD_ReleaseTX(uint32_t ep) { -/* UEINTX = 0x3A; // FIFOCON=0 NAKINI=0 RWAL=1 NAKOUTI=1 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=0 - clear fifocon = send and switch bank - nakini a clearer - rxouti/killbank a clearer - txini a clearer*/ - - //puts("=> UDD_ReleaseTX\r\n"); + TRACE_UOTGHS(printf("=> UDD_ReleaseTX ep=%d\r\n", ep);) UOTGHS->UOTGHS_DEVEPTICR[ep] = (UOTGHS_DEVEPTICR_NAKINIC | UOTGHS_DEVEPTICR_RXOUTIC | UOTGHS_DEVEPTICR_TXINIC); UOTGHS->UOTGHS_DEVEPTIDR[ep] = UOTGHS_DEVEPTIDR_FIFOCONC; ul_send_fifo_ptr[ep] = 0; @@ -364,7 +316,8 @@ uint32_t UDD_ReadWriteAllowed(uint32_t ep) void UDD_SetAddress(uint32_t addr) { - //printf("=> UDD_SetAddress : setting address to %d\r\n", addr); + TRACE_UOTGHS(printf("=> UDD_SetAddress : setting address to %d\r\n", addr);) + udd_configure_address(addr); udd_enable_address(); } diff --git a/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a b/hardware/arduino/sam/variants/arduino_due_x/libsam_sam3x8e_gcc_rel.a index a4074053f20d58000830479902685325541622eb..f090389d0cc0a45dde4ce8456a90f201b7d923f8 100644 GIT binary patch literal 75318 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%0A-5(k^YQMJvJ!{?H z_#buOEKK-+*0T>kPj%n4QJ8hyEK-yI)2jQZ^sN1(6MN0-f417cX+!-cxB8!by-JgN zn|m!kzkP!5jA2EG`@Tf}@krN&L(!x6ZpnX~xi3C0s;_Kc*Ris#X}&t%jKf(kY6{o< z;M5gewefqmtj4<*@izXt2j0sYyJNmK@uB|9yPlsHwNU0b?-H|EQubO?Br#uA>{NZc>7{gG;+x6n zoA-|Q%*U~E1LX}ImaZHx>|C_} zUgLu#ge8Vk$uZ&rS&>``mJ1rRzP3?7cZ>xEdP`Ace=^hIEsXG#`}{x@{EUbdD_M;e z584le@r;b2(1-O}CC1v8-ZRQis@R`jq-}TaDUQgi=Wl#ilZuyL}=Ti<5NYE^tnGA;nUFyYL03S!Pv;B04(#BPXWY{W}|58 zYXZ$D1nsoG1Q@11wAr3MrbX-Phu_rK4Vvq9wbS};0>jjIC-gCIOdr#w^^L=C>f;)T zt>9^=^^JmI>U+dUAMIdT-(mPoeTP7V<@a+I?oQxp!P8F1`z0_;eGh|Z)F7NSPQRO^ zuiL9x;{9t&pFLNv_5G8D<6J$m>^Y752XM@CIcby&mbiQ6at3}gAMq^ueqibQnxT&~ zRg9O{Pro~*^VAM_}n(^KUT-fz!v2a6KaC-l19WUqL59>&A*71G_`pj~f zMm|`6tbaV~eB5Hi`!@8|K(3wEx8KtDs!=YqBWr!XYUzvP@jt9y-EQfdHS}?YOY6JO z($@}srlmor?{k*Ec|#v(ytKYATl#K;zH*&eg(LZhg}XZo?i&{F3&5E(G&+6LR{FRR zCF>EdM->j8K5g6A*LOOLKHgvE_02c*)foC{E&Gt?&4*ZV;a<@qWLoYpWZEdocy^`4az_r_zs!EUuXd;@!QO zT0MMF%KamQoFXede#%_^i!J@GaT1g1obbs z(!X%cMG$|1gw}+jhT~S6cW=3twg7tE}`_S@;?YUt{6@oHHQTOa$|L znU$W)Ed7^T_{$~UFU89(e65AAm3aTIHCuM3XSJ03N>+P)P7F*(u0Zgm{7y>nj=sVh zJ5sYFcL+A|Yb^X4Pos-pxF*97&GcO<-Xrz8&njFap`SZ=&ABGxdL{q1r1RyvJ9wod zW>WciBK--A{*p!iDQNZyTXAsB#FLi%Zq%LLba6CaoIWujTpZusTm`T%`_7*h0MiLVfP9Lk!=H4u8u zgqz+~LXRuimzUyx{GijGVCYZntA0v&M`XtXi>K)(zq~7txqS~*M->=^B zO{#aC2h=;#2*-NMaO~5l=Qf_xLQL_z*S(L(=lwujojUV=AT7_3Usd1ePK~JFv%UM< zxbbR*Jo{YA+lUL!n@i!!R@^4%;Y_;RCNQjm|J=Xhq@NYo?!{R4%4vTaXkHW64?4Ez z#A-(wj~zJ7=bd5D=kmPsdEmH4TswLFLGWxo@7x9ALGbu|!-MH7R(_oakYX(ebA_VT zN15qN{=4zKv)oi3C`pzJX66mY=N&vwOUHba5brmh*pszy5 A3jhEB delta 12672 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