USB Host Mouse Driver

Status
Not open for further replies.
@Paul - I simplified the USBSerial claim code where all of them that claim by VID:pID, use the same code to search for the right input and output descriptors, open the pipes... The only difference after that is which packet to send first and setup for which packets will have to be sent after that... Right now doing another quick round of testing, before I check this update in.

One thing I have found during this testing, which I don't think is particular to these changes nor USBSerial in particular. Is I can typically only plug in a Serial device and remove it twice before on the 2nd (sometimes 3rd or 4th) unplug the code hangs. It is hanging in the USBHost::delete_Pipe code. I have instrumented the code with several more Print statements.
Code:
void USBHost::delete_Pipe(Pipe_t *pipe)
{
	println("delete_Pipe ", (uint32_t)pipe, HEX);

	// halt pipe, find and free all Transfer_t

	// EHCI 1.0, 4.8.2 page 72: "Software should first deactivate
	// all active qTDs, wait for the queue head to go inactive"
	//
	// http://www.spinics.net/lists/linux-usb/msg131607.html
	// http://www.spinics.net/lists/linux-usb/msg131936.html
	//
	// In practice it's not feasible to wait for an active QH to become
	// inactive before removing it, for several reasons.  For one, the QH may
	// _never_ become inactive (if the endpoint NAKs indefinitely).  For
	// another, the procedure given in the spec (deactivate the qTDs on the
	// queue) is racy, since the controller can perform a new overlay or
	// writeback at any time.

	bool isasync = (pipe->type == 0 || pipe->type == 2);
	if (isasync) {
		// find the next QH in the async schedule loop
		Pipe_t *next = (Pipe_t *)(pipe->qh.horizontal_link & 0xFFFFFFE0);
		if (next == pipe) {
			// removing the only QH, so just shut down the async schedule
			println("  shut down async schedule");
			USBHS_USBCMD &= ~USBHS_USBCMD_ASE; // disable async schedule
			while (USBHS_USBSTS & USBHS_USBSTS_AS) ; // busy loop wait
			USBHS_ASYNCLISTADDR = 0;
		} else {
			// find the previous QH in the async schedule loop
			println("  remove QH from async schedule");
			Pipe_t *prev = next;
			while (1) {
				Pipe_t *n = (Pipe_t *)(prev->qh.horizontal_link & 0xFFFFFFE0);
				println("    n:", (uint32_t)n, HEX);
				if (n == pipe) break;
				prev = n;
			}
			println("    * Found Pipe in list");
			// if removing the one with H bit, set another
			if (pipe->qh.capabilities[0] & 0x8000) {
				prev->qh.capabilities[0] |= 0x8000; // set H bit
			}
			// link the previous QH, we're no longer in the loop
			prev->qh.horizontal_link = pipe->qh.horizontal_link;
			// do the Async Advance Doorbell handshake to wait to be
			// sure the EHCI no longer references the removed QH
			USBHS_USBCMD |= USBHS_USBCMD_IAA;
			while (!(USBHS_USBSTS & USBHS_USBSTS_AAI)) ; // busy loop wait
			USBHS_USBSTS = USBHS_USBSTS_AAI;
			// TODO: does this write interfere UPI & UAI (bits 18 & 19) ??
		}
		// find & free all the transfers which completed
		println("  Free transfers");
		Transfer_t *t = async_followup_first;
		while (t) {
			print("    * ", (uint32_t)t);
			Transfer_t *next = t->next_followup;
			if (t->pipe == pipe) {
				println(" * remove");
				remove_from_async_followup_list(t);
				free_Transfer(t);
			} else {
				println("");
			}
			t = next;
		}
	} else {
		// remove from the periodic schedule
		for (uint32_t i=0; i < PERIODIC_LIST_SIZE; i++) {
			uint32_t num = periodictable[i];
			if (num & 1) continue;
			Pipe_t *node = (Pipe_t *)(num & 0xFFFFFFE0);
			if (node == pipe) {
				periodictable[i] = pipe->qh.horizontal_link;
				continue;
			}
			Pipe_t *prev = node;
			while (1) {
				num = node->qh.horizontal_link;
				if (num & 1) break;
				node = (Pipe_t *)(num & 0xFFFFFFE0);
				if (node == pipe) {
					prev->qh.horizontal_link = node->qh.horizontal_link;
					break;
				}
				prev = node;
			}
		}
		// TODO: subtract bandwidth from uframe_bandwidth array

		// find & free all the transfers which completed
		Transfer_t *t = periodic_followup_first;
		while (t) {
			Transfer_t *next = t->next_followup;
			if (t->pipe == pipe) {
				remove_from_periodic_followup_list(t);
				free_Transfer(t);
			}
			t = next;
		}
	}
	//
	// TODO: do we need to look at pipe->qh.current ??
	//
	[COLOR="#FF0000"]// free all the transfers still attached to the QH
	println("  Free transfers attached to QH");
	Transfer_t *tr = (Transfer_t *)(pipe->qh.next);
	while ((uint32_t)tr & 0xFFFFFFE0) {
		println("    * ", (uint32_t)tr);
		Transfer_t *next = (Transfer_t *)(tr->qtd.next);
		free_Transfer(tr);
		tr = next;
	}
	// hopefully we found everything...
	free_Pipe(pipe);
	println("* Delete Pipe completed");[/COLOR]
}
And it is sometimes hanging in the freeing transfers still attached to the QH.

I am wondering if there is a timing issue, like maybe we grab the first one on the list, and start to free it and maybe Interrupt happens that does something to it and the next pointer gets corrupted...

Example Run: Plug in FTDI cable and unplug first time:
Code:
port change: 10001803
    connect
  begin reset
port change: 10001005
  port enabled
  end recovery
new_Device: 12 Mbit/sec
new_Pipe
enumeration:
enumeration:
enumeration:
enumeration:
enumeration:
enumeration:
enumeration:
enumeration:
Config data length = 32
enumeration:
bNumInterfaces = 1
bConfigurationValue = 1
enumeration:
USBHub memory usage = 960
USBHub claim_device this=1FFF2A00
USBHub memory usage = 960
USBHub claim_device this=1FFF3480
HIDParser claim this=1FFF2040
HIDParser claim this=1FFF2520
HIDParser claim this=1FFF3840
USBSerial claim this=1FFF2E40
vid=403, pid=6001, bDeviceClass = 0, bDeviceSubClass = 0, bDeviceProtocol = 0
09 04 00 00 02 FF FF FF 02 07 05 81 02 40 00 00 07 05 02 02 40 00 00 
len = 23
USBSerial, rxep=1, txep=2
new_Pipe
new_Pipe
*** Device USERIAL1 403:6001 - connected ***
  manufacturer: FTDI
  product: TTL232R-3V3
  Serial: FTAKL6VR
control callback (serial) F
control callback (serial) E
control callback (serial) C
control callback (serial) 8
control callback (serial) 0
rx: F8 
�Serial Available
txtimer
  TX data (13) 54 65 73 74 20 6F 66 20 46 54 44 49 0A 
tx1:
rx: 54 65 73 74 20 6F 66 20 46 54 44 49 0A 
Test of FTDI
Serial Available
txtimer
  TX data (12) 53 65 63 6F 6E 64 20 6C 69 6E 65 0A 
tx1:
rx: 53 65 63 6F 6E 64 20 6C 69 6E 65 0A 
Second line
port change: 1C00100A
    disconnect
disconnect_Device:
USBDriver (available_drivers) list: 1FFF2A00 -> 1FFF3480 -> 1FFF2040 -> 1FFF2520 -> 1FFF3840
USBDriver (dev->drivers) list: 1FFF2E40
disconnect driver 1FFF2E40
USBDriver (available_drivers) list: 1FFF2E40 -> 1FFF2A00 -> 1FFF3480 -> 1FFF2040 -> 1FFF2520 -> 1FFF3840
delete_Pipe 1FFF2F20
  remove QH from async schedule
    n:1FFF2EC0
    n:1FFF2F20
    * Found Pipe in list
  Free transfers
    * 536822848 * remove
    * 536822656 * remove
  Free transfers attached to QH
    * 536822656
    * 536822848
    * 536822720
    * 536822784
    * 536817728
    * 536817664
    * 536817600
    * 536817536
    * 536820896
    * 536820832
    * 536820768
    * 536820704
    * 536816000
    * 536815936
    * 536815872
    * 536815808
    * 536814752
    * 536814688
    * 536814624
    * 536814560
    * 536819488
    * 536819424
    * 536819360
    * 536819296
    * 536816800
    * 536816736
    * 536816672
    * 536816608
* Delete Pipe completed
delete_Pipe 1FFF2EC0
  remove QH from async schedule
    n:1FFF2EC0
    * Found Pipe in list
  Free transfers
  Free transfers attached to QH
    * 536817792
* Delete Pipe completed
delete_Pipe 1FFF4300
  shut down async schedule
  Free transfers
  Free transfers attached to QH
    * 536817856
* Delete Pipe completed
removed Device_t from devlist
  disable
*** Device USERIAL1 - disconnected ***
Now I plug it in a 2nd time, Type in some text as I have RX connected to TX line... Then unplug.

Code:
port change: 10001803
    connect
  begin reset
port change: 10001005
  port enabled
  end recovery
new_Device: 12 Mbit/sec
new_Pipe
enumeration:
enumeration:
enumeration:
enumeration:
enumeration:
enumeration:
enumeration:
enumeration:
Config data length = 32
enumeration:
bNumInterfaces = 1
bConfigurationValue = 1
enumeration:
USBSerial claim this=1FFF2E40
vid=403, pid=6001, bDeviceClass = 0, bDeviceSubClass = 0, bDeviceProtocol = 0
09 04 00 00 02 FF FF FF 02 07 05 81 02 40 00 00 07 05 02 02 40 00 00 
len = 23
USBSerial, rxep=1, txep=2
new_Pipe
new_Pipe
*** Device USERIAL1 403:6001 - connected ***
  manufacturer: FTDI
  product: TTL232R-3V3
  Serial: FTAKL6VR
control callback (serial) F
control callback (serial) E
control callback (serial) C
control callback (serial) 8
control callback (serial) 0
Serial Available
txtimer
  TX data (46) 49 20 68 61 76 65 20 61 20 6C 6F 6F 70 62 61 63 6B 20 77 69 72 65 20 61 6E 64 20 74 79 70 65 64 20 69 6E 20 74 68 69 73 20 6C 69 6E 65 0A 
tx1:
rx: 49 20 68 61 76 65 20 61 20 6C 6F 6F 70 62 61 63 6B 20 77 69 72 65 20 61 6E 64 20 74 79 70 65 64 20 69 6E 20 74 68 69 73 20 6C 69 6E 65 0A 
I have a loopback wire and typed in this line
port change: 1C00100A
    disconnect
disconnect_Device:
USBDriver (available_drivers) list: 1FFF2A00 -> 1FFF3480 -> 1FFF2040 -> 1FFF2520 -> 1FFF3840
USBDriver (dev->drivers) list: 1FFF2E40
disconnect driver 1FFF2E40
USBDriver (available_drivers) list: 1FFF2E40 -> 1FFF2A00 -> 1FFF3480 -> 1FFF2040 -> 1FFF2520 -> 1FFF3840
delete_Pipe 1FFF2EC0
  remove QH from async schedule
    n:1FFF4300
    n:1FFF2EC0
    * Found Pipe in list
  Free transfers
    * 536816672 * remove
    * 536816736 * remove
  Free transfers attached to QH
    * 536816736
    * 536816672
    * 536819296
    * 536817792
    * 536819360
    * 536819424
    * 536819488
    * 536814560
    * 536814624
    * 536814688
    * 536814752
    * 536815808
    * 536815872
    * 536815936
    * 536816000
    * 536820704
    * 536820768
    * 536820832
    * 536820896
    * 536817536
    * 536817600
    * 536817664
    * 536817728
    * 536822784
    * 536822720
    * 536822848
    * 536822656
    * 536822656
    * 536822848
    * 536822720
    * 536822784
    * 536817728
    * 536817664
    * 536817600
    * 536817536
    * 536820896
    * 536820832
    * 536820768
    * 536820704
    * 536816000
    * 536815936
    * 536815872
    * 536815808
    * 536814752
    * 536814688
    * 536814624
    * 536814560
    * 536819488
    * 536819424
    * 536819360
    * 536817792
    * 536819296 
... (hangs printing numbers)
Still investigating. But you obviously know a whole lot more about this part of the code!
 
USBSerial - Queue size - buffering - flush() - and txtimer.

@Paul (and everyone) - Not sure if it is best to ask here or email or new thread.

Doing another round of testing of USBSerial code. Thought I would try more real live cases to verify things work as expected. I already mentioned the hang in the delete pipe in previous post.

Overview: One test case.
Thought I would try as a test case, modify my AX Servo test program, to try to talk through the USB Serial port instead of one of the Hardware serial ports. Maybe not something I would do in a live case, but maybe... So I try plugging in a USB2AX device into USB port. This is a Atmega32u2 using LUFA. It comes up as a CDCACM device.
Both RX and TX end points have their size fields set to 16... So the init init_buffers is set for rx1, rx2, tx1, tx2 set to 16 and the rxsize and txsize set to 292. Note: Baud rate set to 1000000

For many cases with the device 16 bytes is fine: They often are simple binary packets, like: TX data (8) FF FF FD 04 02 24 02 D6
With most of these packets we wait for a reply from the device in this case it comes back: rx: FF FF FD 02 08 F8 FF FF FD 04 00 82 08 74
I asked the controller (ID = 0xFD) for two bytes starting at register 36 and it returned two bytes... In the case the request is for a servo that does not exist, we do not receive a response and the code times out and returns an error (-1)
Code:
  for (int i = 0; i < 254; i++) {
    int servo_pos = ax12GetRegister(i, AX_PRESENT_POSITION_L, 2); 
    if (servo_pos != -1) {
      Serial.printf("Servo: %d Pos: %d\n", i, servo_pos);
    }

For hardware Serial port the code does the SerialX.write(packet); SerialX.flush(); Try to do SerialX.read (packet), testing for a timeout.
On Linux I do similar, the logical flush helps on FTDI due to it's latency, but is detrimental on some other devices like /dev/ttyACM0 (like to the USB2AX). Probably as the FTDI driver I believe has support to say, it has completed it's output of the buffers, whereas the ACM one does not appear to and I think just puts in a multi millisecond delay.

Time outs and flush()

Issue 1: Current write timeout (for partial packets) is hard coded to 3500us. Which for cases like this feels too long.
Was thinking of adding a couple of member functions like:
Code:
	enum { DEFAULT_WRITE_TIMEOUT = 3500};
	uint32_t writeTimeout() {return write_timeout_;}
	void writeTimeOut(uint32_t write_timeout) {write_timeout_ = write_timeout;} // Will not impact current ones.
Which allows me to query and set it. Sound Reasonable?

Issue 2: userial.flush() does nothing. {}
Several options here:
a) Punt - Obviously the easiest solution.
b) Slight minimal change - Just call off to timer_event to hopefully issue one transfer request. (more on this function in a different issue)
c) do like b), but also try to understand when the whole write completes and only then return from the function. This is obviously the closest to what a hardware serial flush does.

Thoughts?

timer_event and txtimer
Each time you do a USBSerial::write(c) - The function stops and restarts the txtimer, such that if no other write happens before the timeout value, the timer is triggered and the timer_event is called. Which makes sense.

1) But no where is this timer stopped. At a minimum I think I should be able to stop it in the case that (head==tail). It would be restarted if the user does another write.

2) Wondering about the case in timer_event where logically (txstate & 3 == 3) (No buffers available) - Currently the code changes the timer to 1200 and returns... Which will call this maybe several times before it finds a buffer to use... What I am wondering is if this should instead by handled by the tx_data function?
Where timer_event function simply set a flag, that tx_data method you change the code:
Code:
	if (count < packetsize) {
		// not enough data in buffer to fill a full packet
		txstate &= ~mask;
		return;
	}
To maybe something like:
Code:
	if (count == 0) || ((count < packetsize) && !flush_data_)) {
		// not enough data in buffer to fill a full packet
		txstate &= ~mask;
		flush_data_ = false; // We finished the flush. 
		return;
	}
Note: In many cases we may also be able to clear the flush_data_ if the queue of data later took care of the whole remaining data.

Does this make sense? If so the flush() above might be able to be fully completed if we do something like:
Code:
void USBSerial::flush() {
  if (head == tail) return;  // empty.
  NVIC_DISABLE_IRQ(IRQ_USBHS);
  txtimer.stop();  // Don't need the timer as we will call directly
  flush_data = true;
  timer_event(nullptr);   // queue up the first one... 
  NVIC_ENABLE_IRQ(IRQ_USBHS);

  while (txstate & 3) ; // wait for all of the USB packets to be sent. 
}
Thoughts?

Again is this level of details interesting on forum posts or better with email? or google hangouts or ...
 
Quick update to the above message.

Decided the best way to answer some of the above is to dig in and try it. I think I have most of it in place, and I am hacking on my own Bioloid Servo controller code plus test app to try it out.

It helped uncover a few issues, some of which I have addressed.

Right now I am trying to understand how Serial data is sent back from the device to us... i.e. how rx_data is supposed to work. In particular in the count of actual data bytes in a packet.

Currently the code gets this by: uint32_t len = transfer->length - ((transfer->qtd.token >> 16) & 0x7FFF);

Which if you look at the Enhanced Host Controller Document (like: https://www.intel.com/content/dam/w...specifications/ehci-specification-for-usb.pdf)
And look at the QTD definition we see bits 16-30 are the count to be subtracted from the transfer->length... so this makes sense.

You have code in that if it is FTDI if count> 2 you subtract 2 from it... Not sure if this is specific to FTDI?

The issue. When I am receiving data back from USB2AX (CDCACM Atmega32u2), I am getting some interesting results. Not sure if it is by design or something else.
I do the query for several of the registers of the controller.
The first few work fine: There is additional printing going on...
Code:
0:ax12 ReadPackettxtimer
  TX data (8) FF FF FD 04 02 00 01 FB 
rx token: 98100 transfer length: 16 len:7 - FF FF
rx: FF FF FD 03 00 01 FE 
tx1:
 - valid data
1 1:ax12 ReadPackettxtimer
  TX data (8) FF FF FD 04 02 01 01 FA 
rx token: 80098100 transfer length: 16 len:7 - FF FF
rx: FF FF FD 03 00 42 BD 
tx1:
 - valid data
So we transfer a packet: FF FF FD 04 02 00 01 FB - Read register on device FD Starting register 0 length 1 byte.. FB is checksum
We receive back a packet: where bits 16-30 = 9 and packet size 16 so 7 byte packet: FF FF FD 03 00 42 BD
Which translated in Repsonse for FD and result is 1...

Similar for register 1... - response 0x42.

However many of the other query responses are somewhat strange.
Code:
FD 4:ax12 ReadPackettxtimer
  TX data (8) FF FF FD 04 02 04 01 F7 
tx1:
rx token: 38100 transfer length: 16 len:13 - FF FF
rx: FF FF FD 02 08 F8 FF FF FD 03 00 08 F7 
 - Checksum
FF FF FD 2 8 F8 FF  : FE
FFFFFFFF 5:ax12 ReadPackettxtimer
  TX data (8) FF FF FD 04 02 05 01 F6 
tx1:
rx token: 80038100 transfer length: 16 len:13 - FF FF
rx: FF FF FD 02 08 F8 FF FF FD 03 00 07 F8 
 - Checksum
FF FF FD 2 8 F8 FF  : FE
That is by the logic it says the responses are 13 bytes. Again not sure if this is a something different about CDCACM or just how the USB2AX is working.
Again would expect 7 byte returned.

Note: I may be able to get right data here. That is if you look at the last ones response: FF FF FD 02 08 F8 FF FF FD 03 00 07 F8
There are actually two packets here: FF FF marks start, FD=ID, 02 count of bytes remaining including checksum which we check is wrong... FE should be FF

Currently my code sees this and bails with error. But following this in packet is probably a valid response. I have not actually verified the checksum is
correct. ...

So again wondering if this is some CDCACM thing or maybe USB2AX thing... Will try pulling out a different servo controller which is FTDI based and see what it does.

Will probably also ask up on Trossen forum as the USB2AX developer communicates up there.
Kurt

update Also tried with Arbotix-Pro (FTDI). Don't remember if this one is in a normal configuration. But it is also returning interesting RX data?
Code:
Cmd: 9 200
0:ax12 ReadPackettxtimer
  TX data (8) FF FF C8 04 02 00 01 30 
tx1:
 - timeout
FFFFFFFF 1:ax12 ReadPacketrx token: 80388100 transfer length: 64 len:6 - FF C8
rx: FF C8 03 00 00 34
LIke it it is eating the first FF. Maybe this board is screwed up. Will see if I have a 2nd one that still works.
 
Last edited:
@Paul - Made some progress with the USB2AX... Also fixed how I was trying to do flush. Calling the timer timeout function did not work properly, so instead set minimal timeout, which works. May revisit.

I added several digitalWriteFast calls in the code to be able to trace when things were happening. I converted these to macros debugDigitalWrite and debugDigitalToggle, which if a #define at the top of the Serial.cpp is not set, do nothing... Currently the pinMode stuff is in my test app...

I pushed up most of my current stuff up into the branch with the current Pull request: https://github.com/KurtE/USBHost_t36/tree/Serial-CH341-Plus

I will continue to test using other things. Let me know if you want for me to cleanup some of the github commit stuff and combine them into one or so commits... (git rebase -i ...)

@others - If you have devices that do USB to serial like communications (i.e. they create a COM: serial port on windows or a /dev/ttyUSBx or /dev/ttyACMx on linux), you might try this out and see if we work with those devices.
 
@Paul - Follow up to #176 QH Hang...

If I plug in some of the Serial devices (example a PL2303 adapter). and in the test program issue some text where I have it connect RX to TX, which in my test program the text output is then output to Serial... So shows up in terminal window... Again not sure how much of these steps are necessary... But it appears like at least one message sent and probably received.

When I remove the device it usually does not hang the first time, but if I repeat it almost always hangs... Again added debug code to see what is going on. below is the delete_pipe stuff with debug.

Code:
void USBHost::delete_Pipe(Pipe_t *pipe)
{
	println("delete_Pipe ", (uint32_t)pipe, HEX);

	// halt pipe, find and free all Transfer_t

	// EHCI 1.0, 4.8.2 page 72: "Software should first deactivate
	// all active qTDs, wait for the queue head to go inactive"
	//
	// http://www.spinics.net/lists/linux-usb/msg131607.html
	// http://www.spinics.net/lists/linux-usb/msg131936.html
	//
	// In practice it's not feasible to wait for an active QH to become
	// inactive before removing it, for several reasons.  For one, the QH may
	// _never_ become inactive (if the endpoint NAKs indefinitely).  For
	// another, the procedure given in the spec (deactivate the qTDs on the
	// queue) is racy, since the controller can perform a new overlay or
	// writeback at any time.

	bool isasync = (pipe->type == 0 || pipe->type == 2);
	if (isasync) {
		// find the next QH in the async schedule loop
		Pipe_t *next = (Pipe_t *)(pipe->qh.horizontal_link & 0xFFFFFFE0);
		if (next == pipe) {
			// removing the only QH, so just shut down the async schedule
			println("  shut down async schedule");
			USBHS_USBCMD &= ~USBHS_USBCMD_ASE; // disable async schedule
			while (USBHS_USBSTS & USBHS_USBSTS_AS) ; // busy loop wait
			USBHS_ASYNCLISTADDR = 0;
		} else {
			// find the previous QH in the async schedule loop
			println("  remove QH from async schedule");
			Pipe_t *prev = next;
			while (1) {
				Pipe_t *n = (Pipe_t *)(prev->qh.horizontal_link & 0xFFFFFFE0);
				println("    n:", (uint32_t)n, HEX);
				if (n == pipe) break;
				prev = n;
			}
			println("    * Found Pipe in list");
			// if removing the one with H bit, set another
			if (pipe->qh.capabilities[0] & 0x8000) {
				prev->qh.capabilities[0] |= 0x8000; // set H bit
			}
			// link the previous QH, we're no longer in the loop
			prev->qh.horizontal_link = pipe->qh.horizontal_link;
			// do the Async Advance Doorbell handshake to wait to be
			// sure the EHCI no longer references the removed QH
			USBHS_USBCMD |= USBHS_USBCMD_IAA;
			while (!(USBHS_USBSTS & USBHS_USBSTS_AAI)) ; // busy loop wait
			USBHS_USBSTS = USBHS_USBSTS_AAI;
			// TODO: does this write interfere UPI & UAI (bits 18 & 19) ??
		}
	[COLOR="#0000FF"]	// find & free all the transfers which completed
		println("  Free transfers");
		Transfer_t *t = async_followup_first;
		while (t) {
			print("    * ", (uint32_t)t);
			Transfer_t *next = t->next_followup;
			if (t->pipe == pipe) {
				println(" * remove");
				remove_from_async_followup_list(t);
				free_Transfer(t);
			} else {
				println("");
			}
			t = next;
		}
[/COLOR]	} else {
		// remove from the periodic schedule
		for (uint32_t i=0; i < PERIODIC_LIST_SIZE; i++) {
			uint32_t num = periodictable[i];
			if (num & 1) continue;
			Pipe_t *node = (Pipe_t *)(num & 0xFFFFFFE0);
			if (node == pipe) {
				periodictable[i] = pipe->qh.horizontal_link;
				continue;
			}
			Pipe_t *prev = node;
			while (1) {
				num = node->qh.horizontal_link;
				if (num & 1) break;
				node = (Pipe_t *)(num & 0xFFFFFFE0);
				if (node == pipe) {
					prev->qh.horizontal_link = node->qh.horizontal_link;
					break;
				}
				prev = node;
			}
		}
		// TODO: subtract bandwidth from uframe_bandwidth array

		// find & free all the transfers which completed
		Transfer_t *t = periodic_followup_first;
		while (t) {
			Transfer_t *next = t->next_followup;
			if (t->pipe == pipe) {
				remove_from_periodic_followup_list(t);
				free_Transfer(t);
			}
			t = next;
		}
	}
	//
	// TODO: do we need to look at pipe->qh.current ??
	//
	// free all the transfers still attached to the QH
	[COLOR="#FF0000"]println("  Free transfers attached to QH");
	Transfer_t *tr = (Transfer_t *)(pipe->qh.next);
	while ((uint32_t)tr & 0xFFFFFFE0) {
		println("    * ", (uint32_t)tr);
		Transfer_t *next = (Transfer_t *)(tr->qtd.next);
		free_Transfer(tr);
		tr = next;
	}[/COLOR]
	// hopefully we found everything...
	free_Pipe(pipe);
	println("* Delete Pipe completed");
}

Note while the first delete_pipe here does not hang I think it is not correct.

If you look at when I remove the device we see:
Code:
 remove QH from async schedule
    n:1FFF2EC0
    n:1FFF2F20
    * Found Pipe in list
[COLOR="#0000FF"]  Free transfers
    * 536817792 * remove
    * 536817856 * remove[/COLOR]
 [COLOR="#FF0000"] Free transfers attached to QH
    * 536817856
    * 536817792
    * 536816000
    * 536822848
    * 536822656
    * 536820832
    * 536822720
    * 536815808
    * 536817600
    * 536822784
    * 536817728
    * 536817536
    * 536820896
    * 536814752
    * 536814688
    * 536820768
    * 536815872
    * 536815936
    * 536814624
    * 536814560
    * 536819488
    * 536819424
    * 536819360
    * 536819296
    * 536816800
    * 536816736
    * 536816672
    * 536816608
* Delete Pipe completed[/COLOR]
delete_Pipe 1FFF2EC0
  remove QH from async schedule
    n:1FFF2EC0
    * Found Pipe in list
  Free transfers
  Free transfers attached to QH
    * 536820704
* Delete Pipe completed
delete_Pipe 1FFF4300
  shut down async schedule
  Free transfers
  Free transfers attached to QH
    * 536817664
* Delete Pipe completed
removed Device_t from devlist
  disable
*** Device USERIAL1 - disconnected ***
I set the color to blue for the first remove transfer items, which in the case of this pipe there are some and it removed and freed two transfers. The code and output in RED is deleting the Transfers attached to the QH. Which if you look at, the first one is the last one freed from the the BLUE list and then it goes on trying to free all of the ones in the free list.

When it runs the second time, I think there is a circular loop which hangs...

I may try a hack, to remember the last item I deleted and if it is the same as the one in the free transfers attached to QH, then don't do that list... But not sure what the proper fix is.
Like can items be in the first list that is not in the 2nd?
 
Update - I think I have a fix in for the hang. Pushed it up to my branch.

Updated the code that in the delete pipe, when it removed transfers from the async follow up list, before it called free on that transfer it checked to see if that transfer was also on the QH list. if so it did not free that transfer, but allowed the QH list later to actually free the transfer structure.

Note: I only did this update on the Asynch case not the periodic case. Maybe it also needs it there, but have not run into issues there (yet). But maybe should try other devices out to see.

Edit:Decided to add same testing in the periodic case. Might not need it, but would be nice to avoid these sometimes hard to debug cases.
 
Last edited:
Quick update: I thought I would double check to see if I was properly passing the Baud, Parity, number of data bits and number of stop bits through to the CDCACM class and in particular to a Teensy.

So I programmed a T-LC to the Teensy example program USBtoSerial and was able to test that the baud rates changed.

But: this program does not do anything with the format stuff... Like Parity...

So I was curious and made a version of the program that can handle at least some of the different formats, like 8N1, 7E1, 7O1, and 8n2, and maybe 8O1 and 8E1...

I then used the my Serialtest program part of my usb host branch... And was able to type in request like: #115200,7E1

which is detected, does a userial.end(), and a userial.begin() with the new baud and format.

With the Logic analyzer was able to verify that when the end/begin was called DTR was changed and in this program pin 4 shows a reset.
I also verified that new values for the different format data was properly passed...
With the T-LC - The logic analyzer was happy with the data the LC created on Serial1 for: 8N1, 7E1, 7O1. It did not like the 8N2... But code verified that proper format was passed on Serial1.begin.

So I am happy with this level of capability.

@Paul - Not sure if you would like to update the Example program USBtoSerial?
Code:
/* USB to Serial - Teensy becomes a USB to Serial converter
   http://dorkbotpdx.org/blog/paul/teensy_as_benito_at_57600_baud

   You must select Serial from the "Tools > USB Type" menu

   This example code is in the public domain.
*/

// set this to the hardware serial port you wish to use
#define HWSERIAL Serial1

unsigned long baud = 19200;
#if defined(KINETISK) || defined (KINETISL)
uint8_t numbits = 8;
uint8_t paritytype = 0;
uint8_t stopbits = 1;
#endif
const int reset_pin = 4;
const int led_pin = 13;  // 13 = Teensy 3.X & LC
// 11 = Teensy 2.0
//  6 = Teensy++ 2.0
void setup()
{
  pinMode(led_pin, OUTPUT);
  digitalWrite(led_pin, LOW);
  digitalWrite(reset_pin, HIGH);
  pinMode(reset_pin, OUTPUT);
  Serial.begin(baud);	// USB, communication to PC or Mac
  HWSERIAL.begin(baud);  // communication to hardware serial
}

long led_on_time = 0;
byte buffer[80];
unsigned char prev_dtr = 0;

void loop()
{
  unsigned char dtr;
  int rd, wr, n;

  // check if any data has arrived on the USB virtual serial port
  rd = Serial.available();
  if (rd > 0) {
    // check if the hardware serial port is ready to transmit
    wr = HWSERIAL.availableForWrite();
    if (wr > 0) {
      // compute how much data to move, the smallest
      // of rd, wr and the buffer size
      if (rd > wr) rd = wr;
      if (rd > 80) rd = 80;
      // read data from the USB port
      n = Serial.readBytes((char *)buffer, rd);
      // write it to the hardware serial port
      HWSERIAL.write(buffer, n);
      // turn on the LED to indicate activity
      digitalWrite(led_pin, HIGH);
      led_on_time = millis();
    }
  }

  // check if any data has arrived on the hardware serial port
  rd = HWSERIAL.available();
  if (rd > 0) {
    // check if the USB virtual serial port is ready to transmit
    wr = Serial.availableForWrite();
    if (wr > 0) {
      // compute how much data to move, the smallest
      // of rd, wr and the buffer size
      if (rd > wr) rd = wr;
      if (rd > 80) rd = 80;
      // read data from the hardware serial port
      n = HWSERIAL.readBytes((char *)buffer, rd);
      // write it to the USB port
      Serial.write(buffer, n);
      // turn on the LED to indicate activity
      digitalWrite(led_pin, HIGH);
      led_on_time = millis();
    }
  }

  // check if the USB virtual serial port has raised DTR
  dtr = Serial.dtr();
  if (dtr && !prev_dtr) {
    digitalWrite(reset_pin, LOW);
    delayMicroseconds(250);
    digitalWrite(reset_pin, HIGH);
  }
  prev_dtr = dtr;

  // if the LED has been left on without more activity, turn it off
  if (millis() - led_on_time > 3) {
    digitalWrite(led_pin, LOW);
  }

  // check if the USB virtual serial wants a new baud rate or format
  //  if (Serial.baud() != baud) {
  if ((Serial.baud() != baud)
#if defined(KINETISK) || defined (KINETISL)
      || (Serial.numbits() != numbits)
      || (Serial.paritytype() != paritytype)
      || (Serial.stopbits() != stopbits)
#endif
     ) {
    baud = Serial.baud();

#if defined(KINETISK) || defined (KINETISL)
    numbits = Serial.numbits();
    paritytype = Serial.paritytype();
    stopbits = Serial.stopbits();
    // Convert these into a format field to pass to begin
    // Note We are only handling a subset of the capabilities.
    uint32_t format = SERIAL_8N1; //  This is 8n1
    switch (paritytype) {
      case 0: // no parity;
        if (stopbits == 2) format = SERIAL_8N2;
        break;
      case 1: // Odd parity
        format = (numbits == 7) ? SERIAL_7O1 : SERIAL_8O1;
        break;
      case 2: // even parity
        format = (numbits == 7) ? SERIAL_7E1 : SERIAL_8E1;
        break;
    }
    //Serial.printf("Baud: %d, NB: %d, PT: %d, SB: %d, Format: %x\n", baud, numbits, paritytype, stopbits, format);
#endif
    if (baud == 57600) {
      // This ugly hack is necessary for talking
      // to the arduino bootloader, which actually
      // communicates at 58824 baud (+2.1% error).
      // Teensyduino will configure the UART for
      // the closest baud rate, which is 57143
      // baud (-0.8% error).  Serial communication
      // can tolerate about 2.5% error, so the
      // combined error is too large.  Simply
      // setting the baud rate to the same as
      // arduino's actual baud rate works.
#if defined(KINETISK) || defined (KINETISL)
      HWSERIAL.begin(58824, format);
#else
      HWSERIAL.begin(58824);
#endif
    } else {
#if defined(KINETISK) || defined (KINETISL)
      HWSERIAL.begin(baud, format);
#else
      HWSERIAL.begin(baud);
#endif
    }
  }
}
 
@Paul and others,

Quick Update: I have been playing around with trying to add RAWHID capabilities to USB Host code.

I have a version that is running, up in the branch: https://github.com/KurtE/USBHost_t36/tree/RAWHID2
Note: this branch is based on the branch above mentioned in the last few posts for USBSerial updates.

Not sure what the cleanest approach to adding this support would be, as the USBHIDParser class will typically claim these Devices/Interfaces. - And only after the Interface is claimed and after the HID descriptor is read in and parsed and we find that the Vendor ID and Product ID is of some value and the Report ID is of some value/range do we deduce that it is a RAW hid... Again there may be additional rules for this.

So the RawHIDController class looks at the above conditions and claims the collection. Note: I changed claim_collection to return three different values(enum): CLAIM_NO, CLAIM_REPORT (like true now), and CLAIM_DEVICE (maybe should be CLAIM_INTERFACE).

I added a couple of virtual methods to USBHIDInput:
virtual bool hid_process_in_data(const Transfer_t *transfer) {return false;}
virtual bool hid_process_out_data(const Transfer_t *transfer) {return false;}
That allow HID Input classes the ability to process the data. The in_data one allows them to grab all of the data at once without going through the parse each time. The out_data allows them to get information about when the stuff posted on the out_pipe complete.

As part of this, I also added a pointer to the USBHIDParser on the claim_collection, as for example the RAWHid class needs to be able to use the output pipe.

If we are in the CLAIM_DEVICE state above, I use the end of the 512 byte descriptor field (used to hold the HID descriptor) as two output buffers (out_size).
I am also thinking of using another in_size bytes to add the ability to double buffer inputs. That is currently the in_data code calls off to the hid_process_in_data with the returned transfer structure and only after this returns does it queue_Data_Transfer it back out... Wondering if it should instead Queue the other buffer, then do the callback...

With this, I then wondered, should the USBHIDParser object have enough space in it for the maximum number of Transfer_t objects that it could use in all cases like this, or should it only have enough for the normal cases, like Mouse, or Joystick.

If only more the minimal, than I was thinking that those USBHidInput classes who need more should contribute them. So I added this to the RAW case. But to do so, I needed to move the USBHost::contribute_Transfers to the public section of the class.

I updated the mouse sample app in this branch to add two of the RAW Hid objects. One of them is setup on first pass to hopefully grab the Serial. emulator class and when it receives data on this one it sends it off to our Processors Serial. Note: How I detect this needs work. With the other one I just dump the messages it receives from it to the Serial... And If you type anthing in on Serial, it generates a packet to send out on the RAWSerial...

I Tested this with a T-LC programmed with the basic RAWHID example app.

Not sure who might be interested in it. But it is an interesting experiment. But if you are interested let me know how it goes and if you have some suggestions.

Kurt
 
This morning for the fun of it I tried plugging in an XBOX one controller into my USB Host to see if the USB Joystick hid code would work...

Turns out the XBox one is not a HID setup... So currently the joystick code does not try to claim it...
Here is an edited output showing the descriptors passed into the claim code.
Code:
USBHub claim_device this=1FFF2020
JoystickController claim this=1FFF2A40
JoystickController claim descriptors
  : 09 04 00 00 02 FF 47 D0 00 07 05 02 03 40 00 04 07 05 82 03 40 00 04 09 04 01 00 00 FF 47 D0 00 
  : 09 04 01 01 02 FF 47 D0 00 07 05 03 01 E4 00 01 07 05 83 01 40 00 01 09 04 02 00 00 FF 47 D0 00 
  : 09 04 02 01 02 FF 47 D0 00 07 05 04 02 40 00 00 07 05 84 02 40 00 00
Code checks byte index 5 for value 3 (HID) and in this case they are 0xFF so no match.

To verify this I plugged it into my UP board with Linux and tried to do HID dump and the like which fails.
Here is some of DMESG and lsusb output
Code:
[   99.745378] usb 1-3: USB disconnect, device number 6
[  202.033842] usb 1-3: new full-speed USB device number 7 using xhci_hcd
[  202.163342] usb 1-3: New USB device found, idVendor=045e, idProduct=02ea
[  202.163356] usb 1-3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[  202.163364] usb 1-3: Product: Controller
[  202.163372] usb 1-3: Manufacturer: Microsoft
[  202.163379] usb 1-3: SerialNumber: 3033363030303332393035363234
[  202.165003] input: Generic X-Box pad as /devices/pci0000:00/0000:00:14.0/usb1/1-3/1-3:1.0/input/input5

kurt@kurt-UP-CHT01:~$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 005: ID 0424:2530 Standard Microsystems Corp.
Bus 001 Device 004: ID 0bda:8178 Realtek Semiconductor Corp. RTL8192CU 802.11n WLAN Adapter
Bus 001 Device 003: ID 0424:4603 Standard Microsystems Corp.
Bus 001 Device 007: ID 045e:02ea Microsoft Corp.
Bus 001 Device 002: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

kurt@kurt-UP-CHT01:~$ sudo usbhid-dump
No matching HID interfaces

kurt@kurt-UP-CHT01:~$ ls /dev/input
by-id  by-path  event0  event1  event2  event3  event4  js0  mice
So not HID, but still creates Joystick...

Yes, this is the nightmare of a full USB host implementation... the world is filled with products that don't follow the standards, so they need special drivers. :(

I brought my XBox one controller upstairs today and decided to try again to see how hard it would be to add support for this controller.
So I searched the web and found it was pretty easy to start getting input...
So created a new USB Host device type XBoxOneController class. This class does a claim at the device level by looking at vendor ID and product ID.

I have a table as part of this class, with this, which currently only has my version of controller in it. I then handle the messages with Button/Axis data and map those to similar joystick data.

I added it to the mouse test program and I now am getting some valid data which I am outputting.
I put a current version up on github with new branch, based on hid branch which is based on Serial updates branch...

Again depending on what things you are interested in, can split this off to be based off of master branch.

Some interesting things to think about.

a) would be nice to have a combined Joystick class, but I don't see any easy clear way with current stuff. That is the other joysticks are HID based so the Joystick class is based on USBHidInput. But the XBox code is not HID so it is based on USBDriver.

At times I wonder if some of this should be refactored. That is should USBHIDParser remain as a top level USB object that the user needs to declare. Or should it be some form of worker object, that is contained within other Top level USER objects. Either as subclasses or contained object... Not sure yet as may not know which top level object should claim something until after the claim code completes...

This was also true with the RAWHID code, that is once go through the claim code and get the HID Descriptor and then decide that maybe it is one of the RAWHID areas, I then more or less bypass the rest of the HID code...

b) Naming conventions and the like. I think we still should take a pass through and maybe clean up some of this. In my XBox version I diverged from the Joystick controller naming. That is I changed:
Code:
getButtons -> buttons
getAxis -> axis
joystickDataClear -> clear (might also be dataClear)
If you prefer the get naming convention I can easily convert... Or if you prefer the shorter naming I can take a pass through.
example: I would prefer: myMouse.x() versus myMouse.getMouseX()

Kurt

P.S - I think I may stop looking at some of this USB stuff until you maybe have a chance to catch up some.
 
Update (Again :eek: )

Decided I did not like having separate classes for Joystick and XBox... Sort of like having Keyboard and Keyboard HID Extras.

So decided to try combining the Keyboard classes. So Keyboard has multiple inheritance with:
Code:
class KeyboardController : public USBDriver , public USBHIDInput

I had to rename a few methods, but was able to merge the code and it appears to work:
I updated test app and the like and put it in the WIP XBox branch: https://github.com/KurtE/USBHost_t36/tree/XBox1-support

Next up (probably tomorrow) do the same and merge the XBox class into Joystick.
 
As I mentioned yesterday afternoon, I was going to merge the XBox1 support into the main joystick object. It appears to work :D

Few interesting things I ran into with the multiple inheritance. Some methods like: manufacturer
Are in both base classes. So I added an override in the Joystick object that would handle either case if the object was being used as a top level USB Driver object (XBox1) or as a HID Input device (PS3, PS4, Teensy).

As I said was it yesterday? I think I am done for awhile with USB Host stuff...

All of the current changes are up in: https://github.com/KurtE/USBHost_t36/tree/XBOX1-Keyboard

Also Pull request issued: https://github.com/PaulStoffregen/USBHost_t36/pull/13
Which should replace #11 and #12
 
As I mentioned yesterday afternoon, I was going to merge the XBox1 support into the main joystick object. It appears to work :D

Few interesting things I ran into with the multiple inheritance. Some methods like: manufacturer
Are in both base classes. So I added an override in the Joystick object that would handle either case if the object was being used as a top level USB Driver object (XBox1) or as a HID Input device (PS3, PS4, Teensy).

Thank you very much for the Xbox support! I tested it out today and got it to work without much issue. The only thing I had to change was to add another PID as my Xbox One controller seemed to have a different one:

Code:
//*****************************************************************************
// Support for Joysticks that are class specific and do not use HID
// Example: XBox One controller. 
//*****************************************************************************
// Note: currently just XBOX one. 
JoystickController::product_vendor_mapping_t JoystickController::pid_vid_mapping[] = {
	{ 0x045e, 0x02ea },{ 0x045e, 0x02dd } };
 
Planning to merge this stuff soon, hopefully later today if time permits.

I want to start 1.41 betas later this week, and hopefully get to a stable 1.41 release by Christmas.

If there are other important contributions or bug reports I've missed, please remind me. I do try to keep (admittedly very long) lists of all bug reports that look reproducible, but sometimes I can't keep up with all messages and stuff falls through the cracks.
 
Paul, if you merge would recommend the #13 one instead of the older #11.

With this I merged the two Keyboard objects into one... And likewise the XBox is part of joystick.
This required some cooperation between a top level Keyboard interface as well as with HID code.
 
Thought I would mention, for the fun of it, I am starting to look at how hard it would be to at least partially support a USB Bluetooth adapter and then try to support a few things like, Mouse, Keyboard, Joystick...

I am starting off by capturing some of the initial USB configuration/packet data by sniffing them out using the Saleae Logic Analyzer... Plus analyzing the USB descriptors...

First test is with one of the mini USB adapters I had for some Odroids...

Bus 001 Device 009: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)

Looking at the lsusb -v for this device:
Code:
Bus 001 Device 009: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass          224 Wireless
  bDeviceSubClass         1 Radio Frequency
  bDeviceProtocol         1 Bluetooth
  bMaxPacketSize0        64
  idVendor           0x0a12 Cambridge Silicon Radio, Ltd
  idProduct          0x0001 Bluetooth Dongle (HCI mode)
  bcdDevice           88.91
  iManufacturer           0
  iProduct                2
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength          177
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xe0
      Self Powered
      Remote Wakeup
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           3
      bInterfaceClass       224 Wireless
      bInterfaceSubClass      1 Radio Frequency
      bInterfaceProtocol      1 Bluetooth
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0010  1x 16 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x02  EP 2 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x82  EP 2 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass       224 Wireless
      bInterfaceSubClass      1 Radio Frequency
      bInterfaceProtocol      1 Bluetooth
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x03  EP 3 OUT
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0000  1x 0 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0000  1x 0 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       1
      bNumEndpoints           2
      bInterfaceClass       224 Wireless
      bInterfaceSubClass      1 Radio Frequency
      bInterfaceProtocol      1 Bluetooth
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x03  EP 3 OUT
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0009  1x 9 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0009  1x 9 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       2
      bNumEndpoints           2
      bInterfaceClass       224 Wireless
      bInterfaceSubClass      1 Radio Frequency
      bInterfaceProtocol      1 Bluetooth
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x03  EP 3 OUT
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0011  1x 17 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0011  1x 17 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       3
      bNumEndpoints           2
      bInterfaceClass       224 Wireless
      bInterfaceSubClass      1 Radio Frequency
      bInterfaceProtocol      1 Bluetooth
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x03  EP 3 OUT
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0019  1x 25 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0019  1x 25 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       4
      bNumEndpoints           2
      bInterfaceClass       224 Wireless
      bInterfaceSubClass      1 Radio Frequency
      bInterfaceProtocol      1 Bluetooth
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x03  EP 3 OUT
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0021  1x 33 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0021  1x 33 bytes
        bInterval               1
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       5
      bNumEndpoints           2
      bInterfaceClass       224 Wireless
      bInterfaceSubClass      1 Radio Frequency
      bInterfaceProtocol      1 Bluetooth
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x03  EP 3 OUT
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0031  1x 49 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            1
          Transfer Type            Isochronous
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0031  1x 49 bytes
        bInterval               1
pi@raspberrypi:~ $
First question I have in supporting this, is how do we handle devices which have one interface, but they have multiple alternate configurations of that interface.

That is in this case, there are something like 5 alternate configurations where the main differences are differences in the packet sizes. It is my understanding that you use the
SET_INTERFACE setup packet to choose which one of the alternates you wish to use, which I believe you are supposed to be able to do on devices that have already been initialized... Question is do we support this? ... Next up when would we do this...

Kurt
 
Last edited:
Status
Not open for further replies.
Back
Top