USBHost_t36 Buffer parameter handling

Status
Not open for further replies.

SkryptX

Member
Hello everyone,

I'm currently working with a Teensy 4.0 and doing USB communication. This is generally working, but now I have a problem which I cannot answer myself. It is about the way the USB library handles buffers. I need to do some sendControlPacket(...) calls and this requires a buffer with the bytes sent. My question is how the library handles the buffer passed. It works with a local static buffer, but what does the library do inside? Does it copy the bytes provided? Does it have memory management or just assume that the address with the buffer just exists when the library gets around to sending the bytes out? From what I've seen the library sends data asynchronously.

The question came as I experienced a weird behaviour with it. Im doing this and it works:
Code:
uint8_t buf[] = {
     0xB3, // 0xB3
     3, // Length
     2, // Host type
     1, // Reserved
     1 // Reserved
};
      
bool result = hid.sendControlPacket(0x21, 0x09, 0x0300, 2, 5, buf);

When I wrap this exact code in a function with the declaration
Code:
bool send_b3_packet(USBHIDParser hid_parser);
it doesn't work anymore. But as soon as I put buf as a parameter like so:
Code:
bool send_b3_packet(USBHIDParser hid_parser, uint8_t* buf);
it works again.

My knowledge about C/C++ is okay, but not great and my only guess is that the scope of the function disappears too fast, while the loop scope still exists while the data is read. But this also doesn't seem right. Maybe I'm totally on the wrong path and I just made a error, but any clues are welcome :)

Also how would I program a system where I could manage the buffers if I have to provide them from the outside, when I have no way of knowing when I'm good to delete the buffer.

Thanks in advance!
 
Last edited:
Maybe it would help to see more complete code. like where is buf defined? Is it in a function and as such on the stack?
Does it ever change or is that the actual message? Have you tried making it a static const array instead? ...

I have not looked to much in the host library for awhile so it would require doing things like remembering where things are allocated, also in what logical state I am in. For example in some of the code like joysticks and serial... You don't want to send out multiple packets at the same time, so there are places in their code to remember if they output something and then remember some logical state to then maybe send out the next packet when you have a callback saying that the previous one was output...
 
Here is the whole code... Everything that is commented out doesn't really work. The only way it works is with the uint8_t buf[] = {...}; definition. Malloc/new/in function does not work. This is really strange. I have no clue what it could be and Arduino being Arduino there is no real way to debug this efficiently :(

Also the effective call to sendControlPacket can be in a function or not. That does not seem to matter. Only the origin of the buf.

Code:
#include "USBHost_t36.h"

// Variables
USBHost myusb;
USBHIDParser hid(myusb);

bool hid_active = false;

void setup()
{
  while (!Serial) ; // wait for Arduino Serial Monitor
  Serial.println("\n\nUSB HID Vive Tracker Control");
  myusb.begin();
}


void loop()
{
  myusb.Task();

  if (Serial.available()) {
    int ch = Serial.read(); // get the first char.
    while (Serial.read() != -1) ;

    if (hid_active) {
      if (ch == 's') {
        Serial.println("____Sending B3 packet.");
        /*
        uint8_t buf[] = {
          0xB3, // 0xB3
          3, // Length
          2, // Host type: 2: Phone 3: Accessory
          1, // Reserved
          1 // Reserved
        };*/

        /*uint8_t* buf = (uint8_t*) malloc(5 * sizeof(uint8_t));
        //uint8_t* buf = new uint8_t[5];
        if (buf == NULL) {
          Serial.println("memory alloc failed");
        } 
        buf[0] = 0xB3;
        buf[1] = 3;
        buf[2] = 2;
        buf[3] = 1;
        buf[4] = 1;*/
      
        //bool result = hid.sendControlPacket(0x21, 0x09, 0x0300, 2, 5, buf);
        //bool result = send_b3_packet(hid, buf);
        //Serial.printf("Result: %s\n", result ? "true" : "false");
      } else if (ch == 'b') {
        Serial.println("Sending B4 packet.");
        bool result = send_b4_packet(hid);
        Serial.printf("Result: %s\n", result ? "true" : "false");
      }
    } else {
      Serial.println("Device not connected.");
    }
  }

  if (hid != hid_active) {
    if (hid_active) {
      Serial.printf("*** HID Device disconnected ***\n");
      hid_active = false;
    } else {
      Serial.printf("*** HID Device %x:%x connected ***\n", hid.idVendor(), hid.idProduct());
      hid_active = true;

      const uint8_t *psz = hid.manufacturer();
      if (psz && *psz) Serial.printf("  manufacturer: %s\n", psz);
      psz = hid.product();
      if (psz && *psz) Serial.printf("  product: %s\n", psz);
      psz = hid.serialNumber();
      if (psz && *psz) Serial.printf("  Serial: %s\n", psz);

      delay(1000);

      Serial.println("Sending B3 packet.");
      uint8_t buf[] = {
        0xB3, // 0xB3
        3, // Length
        3, // Host type: 2: Phone 3: Accessory
        1, // Reserved
        1 // Reserved
      };

      bool result = send_b3_packet(hid, buf);
    }
  }
}


bool send_b3_packet(USBHIDParser& hid_parser, void* buf2) {
  /*uint8_t buf[] = {
    0xB3, // 0xB3
    3, // Length
    2, // Host type: 2: Phone 3: Accessory
    1, // Reserved
    1 // Reserved
  };*/
  
  return hid_parser.sendControlPacket(0x21, 0x09, 0x0300, 2, 5, buf2);
};
 
Again it has been awhile since I worked on this library, so I am pretty rusty.

I believe that the buffer you pas in to sendControlPacket is probably during the transfer, So putting it on the stack
like what I think you had before like:
Code:
  uint8_t buf[] = {
    0xB3, // 0xB3
    3, // Length
    2, // Host type: 2: Phone 3: Accessory
    1, // Reserved
    1 // Reserved
  };
  
  return hid_parser.sendControlPacket(0x21, 0x09, 0x0300, 2, 5, buf);
Would screw up as who knows what will be in that stack location when the system tries to actually send it.

As for malloc failing, to work, I am not sure. I am not sure I tried this, and/or if there are parts of USB system that is like DMA where it may not flush the cached data back to phsyical memory...
(More in a few other threads about DMA...)

If you look at some of our stuff that does call sendControlPacket, like in JoystickController, you will see that as part of our class we have a buffer for doing output txbuf_
And for example in the function: JoystickController::transmitPS3UserFeedbackMsg()
We build the message and then call it using our objects buffer.
Code:
    memcpy(txbuf_, PS3_USER_FEEDBACK_INIT, 48);

	    txbuf_[1] = rumble_lValue_? rumble_timeout_ : 0;
	    txbuf_[2] = rumble_lValue_; // Small Rumble
	    txbuf_[3] = rumble_rValue_? rumble_timeout_ : 0; 
	    txbuf_[4] = rumble_rValue_; // Big rumble
	    txbuf_[9] = leds_[2] << 1; // RGB value 	// using third led now...
	    //DBGPrintf("\nJoystick update Rumble/LEDs %d %d %d %d %d\n",  txbuf_[1], txbuf_[2],  txbuf_[3],  txbuf_[4],  txbuf_[9]);
		return driver_->sendControlPacket(0x21, 9, 0x201, 0, 48, txbuf_);

As being Arduino no way to debug... It has lots of ways to debug... Just not Hardware debug... I do a lot of debugging, using things like Serial.print, digitalWrite()...

And in fact we have lots of debug printing and the like built into that library. You simply need to edit USBHost_t36.h at about line 60 and uncomment the line:
//#define USBHOST_PRINT_DEBUG

And then tons of stuff will print out to the USB Serial port. If you are having issues with USB Serial or have other stuff going on there, you can uncomment the next line and set a hardware Serial port like Serial1, and have all of this debug information go out there...

Some of the other files have additional debug that can be turned on, typically by uncommenting either some define like that or some specific print that we commented out.
 
I tried a few different things, but ultimately I just went for a global buffer, which I just update to the newest values since the packets are constant in length and don't need to be sent extremely frequently. That seems to work quite ok and is quite similar to the txbuf_ you mentioned. Except there it also uses a function that's called setTXBuffers which I don't understand. There may be a better way to solve this, but I really have trouble to fully understand the inner workings of this library.

What I also don't really understand is if the library is written with a background buffer or not? Usually the library just takes any random buffer into an "enqueueing" function that copies the contents of that buffer, save it in a ring buffer internally and then write it out. Does this library not do that? Does it really just copy the reference it is given and just roll with it?

For the debugging... I really have to say once you experienced the joy of being able to set breakpoints and do real-time inspections and all of that you just don't wanna go back to the printf/serial way of debugging. For problems like this and other obscure stuff like invalid pointers and so on it is SO helpful to be able to see and inspect even a hard fault. I could just step through the code and see where the memory address goes and why this does or doesn't make sense. I don't say this is design flaw of the Teensy (except maybe the missing pads for soldering the header onto the board). After all I'm using one for the simplicity and ease of programming, but still... sometimes you really wish :D
 
Status
Not open for further replies.
Back
Top