Can't get a manual out endpoint to work

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
ThAl_4704151
Level 4
Level 4
25 sign-ins 25 replies posted 10 replies posted

Hello, 

I'm trying to set up an endpoint, but when I write to the endpoint, I can't read the data from my app or the control center. I'm hoping if I post the code in question, some kind soul can help me figure out what I'm doing wrong.

I've included the function that configures the endpoints and the function that writes log messages to the host machine.

The endpoint in question is 10, and it's an IN endpoint, meaning it goes from the FX3 to the host. It's the last one listed in the descriptor, which means in my list of endpoints it's also the last one. When it isn't clear what value something is, I'll look it up in the debugger and put its value in a comment so you can follow along.

int8_t configureEndpoints()
{
	// Wait until descriptor is parsed in another thread
	CyU3PMutexGet (&descriptorMutex, CYU3P_WAIT_FOREVER);
	CyU3PReturnStatus_t apiRetStatus = CY_U3P_SUCCESS;
	CyU3PEpConfig_t epCfg;
	CyU3PDmaChannelConfig_t dmaCfg;
	CyU3PDmaType_t dmaType;
	struct ConfigurationDescriptor *config = _getCurrentConfig();
	for (uint8_t interfaceIndex = config->firstInterface;
			interfaceIndex < config->firstInterface + config->numInterfaces;
			++interfaceIndex){
		struct InterfaceDescriptor *interface = gInterfaceDesc + interfaceIndex;
		// for each interface in a configuration
		for (uint8_t endpointIndex = interface->firstEndpoint;
				endpointIndex < interface->firstEndpoint + interface->numEndpoints; ++endpointIndex){
			struct EndpointDescriptor *endpoint = gEndpointDesc + endpointIndex;
			// for each endpoint in an interface

			// Configure the corresponding endpoint
			CyU3PMemSet ((uint8_t *)&epCfg, 0, sizeof (epCfg));
			epCfg.enable   = CyTrue;
			epCfg.epType   = endpoint->type;// CY_U3P_USB_EP_INTR
			epCfg.burstLen = 1;
			epCfg.streams  = 0;
			epCfg.pcktSize = endpoint->packetSize; // 528

			apiRetStatus = CyU3PSetEpConfig(endpoint->address, &epCfg); // 0x8a
			switch(apiRetStatus){
			case CY_U3P_ERROR_NOT_STARTED:
				errorLoop();
				break;
			case CY_U3P_ERROR_NULL_POINTER:
				// Should never happen
				errorLoop();
				break;
			case CY_U3P_ERROR_BAD_ARGUMENT:
				// We got a bad endpoint number from the descriptor
				return USB_INVALID_ADDRESS;
			case CY_U3P_ERROR_INVALID_CONFIGURATION:
				// bursts of more than 1 are only supported on certain endpoints
				// We're hardcoding this to false for now anyway, so this should never happen
				errorLoop();
				break;
			}

			// Set up the DMA buffer for the endpoint
			uint8_t epLeadsToHost = endpoint->address & IN_Endpoint; // 0x80, but used as a boolean
			uint8_t addressNum = endpoint->address & ~IN_Endpoint; // 10
			dmaCfg.size           = endpoint->packetSize; // 528
			dmaCfg.count          = DMA_CHANNEL_SIZE / endpoint->packetSize; // 16
			if (epLeadsToHost && addressNum == 10){
				// Debug endpoint
				dmaCfg.notification = 0;
				dmaCfg.prodSckId = CY_U3P_CPU_SOCKET_PROD;
				dmaCfg.consSckId = CY_U3P_UIB_SOCKET_CONS_0 + addressNum;
				dmaType = CY_U3P_DMA_TYPE_MANUAL_OUT;
			}
			else if (epLeadsToHost) {
				// Data goes from the device to the host
				// This block isn't used for the endpoint we're talking about
			}
			else {
				//Data goes from the host to the device
				// This block isn't used for the endpoint we're talking about either
			}
			dmaCfg.dmaMode        = CY_U3P_DMA_MODE_BYTE;

			dmaCfg.cb             = CyFxDmaCallback; // Not used for this endpoint, but shouldn't be called because it has no notifications
			dmaCfg.prodHeader     = 0;
			dmaCfg.prodFooter     = 0;
			dmaCfg.consHeader     = 0;
			dmaCfg.prodAvailCount = 0;
			apiRetStatus = CyU3PDmaChannelCreate(&endpoint->dmaHandle, dmaType, &dmaCfg);
			switch (apiRetStatus){
			case CY_U3P_ERROR_BAD_ARGUMENT:
			case CY_U3P_ERROR_NULL_POINTER:
				// Shouldn't happen
				errorLoop();
				break;
			case CY_U3P_ERROR_MEMORY_ERROR:
				// Could not allocate buffers
				errorLoop();
			}
			if (addressNum == 10){
				debugChannel = &endpoint->dmaHandle;
			}

			// the 0 tells the DMA not to limit the transfers.
			apiRetStatus = CyU3PDmaChannelSetXfer(&endpoint->dmaHandle, 0);
			switch (apiRetStatus){
			case CY_U3P_ERROR_NULL_POINTER:
			case CY_U3P_ERROR_NOT_CONFIGURED:
			case CY_U3P_ERROR_NOT_SUPPORTED:
				// we passed a null pointer, which shouldn't happen
				// our configuration was wrong, which shouldn't happen
				// no buffer was allocated, which shouldn't happen
				errorLoop();
			}

			if (!epLeadsToHost){
				// Configure endpoint watermarks
				endpoint->dmaThreadNum = USB_ADDRESS_TO_PIB(addressNum) % 4;
				apiRetStatus = CyU3PGpifSocketConfigure(
						endpoint->dmaThreadNum,
						CY_U3P_PIB_SOCKET_0 + USB_ADDRESS_TO_PIB(addressNum),
						0, // buffer contains data if watermark is higher than 0
						CyTrue,
						0);
				if (apiRetStatus != CY_U3P_SUCCESS){
					errorLoop();
				}
			}

			// Flush the endpoint
			CyU3PUsbFlushEp(endpoint->address);

			//Configure the GPIF-II socket

		}

	}
	CyU3PMutexPut (&descriptorMutex);
	endpointsAreEnabled = CyTrue;
	if (config->isHighSpeed){ // true
		// The configuration must be high speed. Sending an interrupt without
		// the ready pin asserted indicates that the enumeration was successful,
		// but at the wrong speed
		setReady(CyTrue);
	}
	else {
		setReady(CyFalse);
	}
	// report when it's finished starting up
	extern uint32_t interruptSentTicks;
	interruptSentTicks = CyU3PGetTime();
	interrupt8051(); // Does some GPIO stuff to ping another microcontroller
	breaksAllowed = CyTrue; // First message has been received from host. Who cares if we time out our connection.
	return 0;
}

// We have 16 bytes to work with because the smallest DMA buffer size increments are 16 bytes
#define DEBUG_HEADER_SIZE 15
#define DEBUG_FOOTER_SIZE 1
CyU3PDmaChannel *debugChannel;
void debugLog(int length, unsigned char *data, char endpoint){
#ifdef DEBUG_ENDPOINT10 // is defined
	CyU3PReturnStatus_t error;
	CyU3PDmaBuffer_t buffer;
	// Get a buffer to pass in data
	error = CyU3PDmaChannelGetBuffer (
			debugChannel,                /**< Handle to the DMA channel on which to wait. */
	        &buffer,             /**< Output parameter that will be filled with data about the
	                                                     buffer that was obtained. */
	        CYU3P_NO_WAIT                     /**< Duration to wait before returning a timeout status. */
	        );
	if (error == CY_U3P_ERROR_TIMEOUT || error == CY_U3P_ERROR_MUTEX_FAILURE){ // Usually gets stuck here, failing to get the Mutex
		
		return;
	}
	else if (error != CY_U3P_SUCCESS){
		errorLoop();
	}

	// Copy data over
	int transferLength = length; // should be 8 in this example
	char wasClipped = 0;
	if (length > buffer.size - (DEBUG_HEADER_SIZE + DEBUG_FOOTER_SIZE)){ // header is 15, footer is 1, buffer size is 528
		// Shouldn't happen if my buffer sizes are right, but who knows? Better to be safe
		transferLength = buffer.size - (DEBUG_HEADER_SIZE + DEBUG_FOOTER_SIZE);
		wasClipped = 1;
	}
	// An easy pattern to recognize so we know we have the byte order right
	buffer.buffer[0]  = 0xDE;
	buffer.buffer[1]  = 0xAD;
	buffer.buffer[2]  = 0xBE;
	buffer.buffer[3]  = 0xEF;
	// The endpoint this data is associated with, or 0xff is it's a generic log message
	buffer.buffer[4]  = endpoint; // 0 in this example
	// 1 if the message has been clipped. 0 if it has not
	buffer.buffer[5]  = wasClipped;
	// Reserved for more details later
	buffer.buffer[6]  = 0;
	buffer.buffer[7]  = 0;
	buffer.buffer[8]  = 0;
	buffer.buffer[9] = 0;
	buffer.buffer[10] = 0;
	buffer.buffer[11] = 0;
	buffer.buffer[12] = 0;
	buffer.buffer[13] = 0;
	buffer.buffer[14] = 0;
	// the rest of the data
	memcpy(buffer.buffer + DEBUG_HEADER_SIZE, data, transferLength);
	buffer.count = transferLength + DEBUG_HEADER_SIZE + DEBUG_FOOTER_SIZE;
	// checksum
	unsigned char checksum = 0;
	for (int i = 0; i < transferLength + DEBUG_HEADER_SIZE; ++i){
		checksum += buffer.buffer[i];
	}
	buffer.buffer[transferLength + DEBUG_HEADER_SIZE] = checksum;

	error = CyU3PDmaChannelCommitBuffer (
			        debugChannel,                /**< Handle to the DMA channel to be modified. */
			        buffer.count,                         /**< Size of data in the buffer being committed. The buffer
			                                                     address is implicit and is fetched from the active descriptor
			                                                     for the channel. */
			        0                      /**< Current status (end of transfer bit) of the buffer being
			                                                     committed. The occupied bit will automatically be set by
			                                                     the API. */
			        );
	if (error != CY_U3P_SUCCESS){
		errorLoop();
	}

#endif
}

 

As I said in the comment, at the moment it's failing to get the mutex for some reason I don't understand, but earlier today it was getting farther than that and still nothing went through to the host machine. If you can help me understand what I'm doing wrong, or point out some other things I should check, I'd greatly appreciate it.

0 Likes
6 Replies
AliAsgar
Moderator
Moderator
Moderator
1000 replies posted 250 solutions authored 750 replies posted

Hi,

Could you remove all the Mutex related APIs from the firmware? This is because the getting and putting of Mutex for a particular API call is already taken care of in the library source file.

Also I can notice that, the first parameter of the getBuffer API is "debugChannel". Has 
"&endpoint->dmaHandle" been assigned to "debugChannel", anywhere else in your source code?

 
Best Regards,
AliAsgar

 

 

0 Likes

There's 4 references to debugChannel in the workspace. It's declared at the top of the file these functions come from, it's assigned in the configureEndpoints function when we go through endpoint 10, and it's used only in getBuffer and commitBuffer.

I've moved some calls around so the mutex isn't necessary anymore, but it wasn't protecting the API. My descriptor is a big byte array I copied from our old project, so I have to parse it out to feed the descriptor sections into CyU3PUsbSetDesc(), and while I do that, I copy useful information from it into the structs that we iterate over in the configureEndpoints function. The configureEndpoints function happens in the Event callback when we get a setconf event, so the mutex was making sure that those structs weren't half parsed when the callback happens.

Regardless, I'm still running into the same problem after removing the mutex.

0 Likes

Hi,

Have you confirmed if the issue is due to mutex failure itself and not timeout error?

How frequent is the error? Is it always due to the mutex failure/timeout error issue?

Could you share the firmware source code with us?

Best Regards,
AliAsgar

0 Likes
lock attach
Attachments are accessible only for community members.

The only reason I know it's a mutex failure is that it returns CY_U3P_ERROR_MUTEX_FAILURE every time I've looked at it. If a timeout can cause that too, then maybe it's a timeout.

It happens every time as far as I can tell.

I've attached the firmware source. The debugLog function is defined in usb.c, it's supposed to communicate over endpoint 10, and it gets called every 10 seconds from a timer in main.c.

0 Likes

Hello.

You cannot call CyU3PDmaChannelGetBuffer and CyU3PDmaChannelCommitBuffer in a timer callback.  Do not call debug prints either in a timer callback.

You  can either set a global variable in the timer callback and handle that global variable in your thread or alternatively set an event and have the thread waken on the event.  In either case, the debugLog function should be called from the thread and not the callback.  When that is done, you can read the 24 bytes of data over the IN endpoint 10.

Also note that you can call CyU3PDebugPreamble(CyFalse) after CyFxBulkLpApplnDebugInit() to remove the preamble that doesn't show as ascii characters  in the debug terminal.

Frank

0 Likes

Ok, I got it working and it turned out that calling the debug print during a timer callback was part of the problem like frank said.

I didn't end up needing to use it as a manual endpoint after all, so for the other part, my coworker and I walked through the usbDebug sample project line by line. Ultimately, we found that we had to call CyU3PUsbFlushEp on the endpoint right before calling CyU3PDebugInit. Flushing afterwards didn't work.

Edit: I found that the debug print function didn't work for me because the data I wanted to send has 0s in it, which would be interpreted as null characters.

I also found that I may have been configuring the debug out endpoint as if it were a GPIF endpoint like the others even though it was not. I may also have had the MANUAL_IN and MANUAL_OUT mixed up. In USB terms, IN and OUT are relative to the host, but since the DMA in the FX3 isn't just for USB, manual in and out are relative to it instead.

0 Likes