USB DDK Questions

Has anyone successfully used the USB DDK to write a driver for a USB device (preferably one with input and output operations)?

I want to talk to a USB device from Measurement Computing, a 1208FS which has both input and output commands.

I downloaded the DDK and installed it and looked through the sample files for the mouse, keyboard and printer. Since the printer is the only one with I/O examples, I copied that code and used it as a baseline.

I changed the printer code to look for the vendor/device id’s as reported by usb -vvv. Now when the 1208FS device is inserted or removed it’s detected properly and is happily configured in /dev as /dev/1208FS. So far, so good.

The 1208FS reports itself as doing interrupt transfers (as opposed to Bulk like the printer). So I changed the prn_io() routine to do interrupt transfers instead of bulk ones.

I then took the sample code that was provided for talking to the device under Linux (including the Linux drivers) and compiled it. The big change being stop using the Linux Hiddev commands and instead use the plain open/read/write commands.

When I start my sample code, the usb driver I modified correctly detects my open request and my sample code then attempts to send a command to blink a light on the device. In the usb driver code, I see the command bytes come through properly and the usbd_io() command runs successfully but the light on the device doesn’t blink (I know it works cause I tested it with the Windows exe that was provided).

So I’m stumped as to where I am going wrong since I see all the correct data getting set right up to the usbd_io() routine which says it does the transfer to the usb stack.

Is there something I’m not quite doing right or gotcha that I am not aware of? In theory, it looks really simple to use the examples and create your own USB device driver-as-a-resource manager.

Tim

P.S. Does anyone know of a way to ‘sniff the USB wire’ under QNX either via software or a hardware device in a manner similar to tcpdump?

Maybe something is wrong in the linux source code…

I don’t know of any way to sniff packet via software tool but there is some special hardware for that . I once wrote a driver for a printer and we rented a USB sniffer, it was an invaluable tool.

Mario,

I suspect the error is in my translation of that code and/or my understanding of USB (which is VERY limited).

I attached a software sniffer under Windows to see what is being sent there to the device when the light blink test is done. This is what I get:

Bus Hound 5.05 capture on Windows XP Service Pack 2. Complements of www.perisoft.net

Device - Device ID (followed by the endpoint for USB devices)
(21) USB Human Interface Device
(22) USB Human Interface Device
(23) USB Human Interface Device
(24) USB Human Interface Device
Length - Total transfer length
Phase - Phase Type
CTL USB control transfer
OUT Data out transfer
URB USB request block
Data - Hex dump of the data transferred
Descr - Description of the phase
Cmd… - Position in the captured data

Device  Len    Phase  Data                   Description       Cmd.Phase.Ofs(rep)
------  --------  -----  -----------------------  ----------------  ------------------
 21.0 ---  CTL    21 09 40 02  00 00 02 00    SET REPORT   1.1.0        
 21.0  2   OUT    40 00                                @.                 1.2.0        
 21.0 ---  URB    50 00 08 00  00 00 00 00 CTRLXFER         1.3.0        

From this, I see there are only 2 data bytes sent by the program (0x40 and 0x00) during the OUT phase. This is consistent with what I see under Linux as the command to blink the light is indeed 0x40 (which I then assume is sent as a 16bit number).

Now, obviously, the rest of the data (1st and 3rd lines of which the 1st and 3rd lines only shows the 1st 8 bytes due to screen size) is generated by the USB stack. That’s where my limited knowledge of USB comes into play.

I’m assuming that those 2 lines are similar to how TCP/IP protocol works with regards to sending ethernet packets. Ie, I just write data to a socket and the TCP/IP stack takes care of generating the ethernet packets with all the related overhead. I’m assuming the USB stack in QNX does the same. So I might be wildly offbase with that assumption.

But I also have some questions that aren’t documented (oh to have those DDK examples have a few comment lines…).

  1. The device says it’s packet size is 64 bytes. Yet I see no way to know how to specify that other than in the usbd_setup_interrupt() call. I am not sure if the length field there is meant to be 64 or the bytes I am tranferring. I’ve tried both with no success. But I am curious to how the USB stack knows how to deal with that.

  2. Under Windows, the when the device is inserted it morphs into 4 interfaces (not sure what you really call it under USB terminology). So when I attached the sniffer, I can attach from 1-4 places on the device. Now, when I do a ‘usb -vvv’ command under QNX, I also see 4 interfaces with start/endpoints. When I start my driver, it does indeed get a ‘device insertion’ call done 4 times (once for each interface). But when I try to attach to those interfaces, the 1st and 4th one fails with EBUSY so I only end up with 2 instances under /dev to open instead of 4. I have no idea why I get the EBUSY, but I do know that the command I want to write is meant to go to the 1st instance (looking at the test code provided) which could be why it’s not working for me.

Here is what my usb -vvv looks like with regards to my device:

USB 2 (UHCI) v1.10, v1.01 DDK, v1.01 HCD
    Control, Interrupt, Bulk, Isoch, Low speed, Full speed

Device Address             : 1
Upstream Host Controller   : 2
Upstream Device Address    : 0
Upstream Port              : 1
Upstream Port Speed        : Full
Vendor                     : 0x09db (MCC)
Product                    : 0x0082 (USB-1208FS)
Device Release             : r1.00
USB Spec Release           : v2.00
Serial Number              : N/A
Class                      : 0x00 (Independant per interface)
Max PacketSize0            : 64
Languages                  : 0x0409 (English)
Current Frame              : 938 (1024 bytes)
Configurations             : 1
  Configuration            : 1 (Default)
    Attributes             : 0x80 (Bus-powered)
    Max Power              : 500 mA
    Interfaces             : 4
      Interface            : 0 / 0 (Interface 0)
        Class              : 0x03 (HID)
        Subclass           : 0x00
        Protocol           : 0x00
        Endpoints          : Control + 1
          Endpoint         : 0
            Attributes     : Control
            Max Packet Size: 64
          Endpoint         : 1
            Attributes     : Interrupt/IN
            Max Packet Size: 64
            Interval       : 1 ms
      Interface            : 1 / 0 (Interface 1)
        Class              : 0x03 (HID)
        Subclass           : 0x00
        Protocol           : 0x00
        Endpoints          : Control + 2
          Endpoint         : 0
            Attributes     : Control
            Max Packet Size: 64
          Endpoint         : 2
            Attributes     : Interrupt/OUT
            Max Packet Size: 64
            Interval       : 1 ms
          Endpoint         : 3
            Attributes     : Interrupt/IN
            Max Packet Size: 64
            Interval       : 1 ms
      Interface            : 2 / 0 (Interface 2)
        Class              : 0x03 (HID)
        Subclass           : 0x00
        Protocol           : 0x00
        Endpoints          : Control + 1
          Endpoint         : 0
            Attributes     : Control
            Max Packet Size: 64
          Endpoint         : 4
            Attributes     : Interrupt/IN
            Max Packet Size: 64
            Interval       : 1 ms
      Interface            : 3 / 0 (Interface 3)
        Class              : 0x03 (HID)
        Subclass           : 0x00
        Protocol           : 0x00
        Endpoints          : Control + 1
          Endpoint         : 0
            Attributes     : Control
            Max Packet Size: 64
          Endpoint         : 5
            Attributes     : Interrupt/IN
            Max Packet Size: 64
            Interval       : 1 ms

Tim

Endpoints parsing code please:

JIV,

Understand that I took the printer.c template code from the DDK and basically did some minor mod’s to it. Here is that result.

The “TDS” print lines are my comment lines to track how the code is behaving.


int prn_parse_descriptors( prn_t *prn )
{
	_uint32						eix;
	_uint32						scan;
	_uint32						found;
	usbd_descriptors_t			*desc;
	usbd_device_descriptor_t	*ddesc;
	usbd_interface_descriptor_t	*idesc;
	struct usbd_desc_node		*dev, *ifc, *ept;
	int							alternate;

	printf ("TDS: prn_parse_descriptors()\n");
	
	scan	= PRN_CONTROL_EP | PRN_INTIN_EP;
	found	= 0;

// Original printer.c code
//	if( ddesc = usbd_device_descriptor( prn->device, &dev ) ) {

//		prn->bcdUSB = ddesc->bcdUSB;
		// look for interface with bi-directional or uni-directional protocol (No support  for IEEE 1284.4 multi function devices)
//		for ( alternate = 0 ; ( idesc = usbd_interface_descriptor( prn->device, prn->instance.config, prn->instance.iface, alternate, &ifc ) ) ; alternate++ ) {
//			if( (idesc->bInterfaceProtocol != USB_PROTOCOL_PRN_BIDIR) && (idesc->bInterfaceProtocol != USB_PROTOCOL_PRN_UNIDIR)  ) 
//				continue;
				
//			if( ( prn->protocol = idesc->bInterfaceProtocol ) == USB_PROTOCOL_PRN_BIDIR ) {
//				scan |= PRN_BULKIN_EP;
//			}
//			usbd_select_interface( prn->device, prn->instance.iface, alternate );

	if( usbd_interface_descriptor( prn->device, prn->instance.config, prn->instance.iface, prn->instance.alternate, &ifc ) ) {

			usbd_select_interface( prn->device, prn->instance.iface, 0 );
			for( eix = 0; ( desc = usbd_parse_descriptors( prn->device, ifc, USB_DESC_ENDPOINT, eix, &ept ) ) != NULL; ++eix ) {
				switch( desc->endpoint.bmAttributes ) {
					case USB_ATTRIB_CONTROL:
				        printf ("TDS: USB_ATTRIB_CONTROL for interface %d\n", prn->instance.iface);
						if( usbd_open_pipe( prn->device, desc, &prn->ep_cntl ) == EOK ) {
							found |= PRN_CONTROL_EP;
						}
						break;

					case USB_ATTRIB_ISOCHRONOUS:
				        printf ("TDS: USB_ATTRIB_ISCOHRONOUS\n");
						break;

					case USB_ATTRIB_BULK:
				        printf ("TDS: USB_ATTRIB_BULK\n");
						switch( desc->endpoint.bEndpointAddress & USB_ENDPOINT_IN ) {
											break;
						}
						break;

					case USB_ATTRIB_INTERRUPT:
				        printf ("TDS: USB_ATTRIB_INTERRUPT for interface %d\n", prn->instance.iface);
					    switch( desc->endpoint.bEndpointAddress & USB_ENDPOINT_IN ) {
					         case USB_ENDPOINT_OUT:
						         printf ("TDS: INTERRUPT endpoint out\n");
							     if( usbd_open_pipe( prn->device, desc,
									&prn->ep_out ) == EOK ) {
									 prn->ep_int_size = desc->endpoint.wMaxPacketSize;
								 }
							     else
							     {
									 printf ("TDS: INTERRUPT endpoint out - open pipe failed with %d\n", errno);
							     }
							     break;

						     case USB_ENDPOINT_IN:
							     if( usbd_open_pipe( prn->device, desc,
									&prn->ep_in ) == EOK ) {
									 prn->ep_int_size = desc->endpoint.wMaxPacketSize;
									 found |= PRN_INTIN_EP;
									 printf ("TDS: INTERRUPT endpoint in with max size %d\n", prn->ep_int_size);
								 }
							     else
							     {
									 printf ("TDS: INTERRUPT endpoint in - open pipe failed with %d\n", errno);
							     }
							     break;
					    }
						break;
				}
			}
//			if( found == scan )
//				break;
//		}
	}
	else
	{
		printf ("TDS: Error\n");
	}

	return( ( found == scan ) ? EOK : ENODEV );
}

When the code runs I see the following printed:

1st Insertion call fails

For the 2nd insertion call
TDS: prn_parse_descriptors()
TDS: USB_ATTRIB_CONTROL for interface 1
TDS: USB_ATTRIB_INTERRUPT for interface 1
TDS: INTERRUPT endpoint out
TDS: USB_ATTRIB_INTERRUPT for interface 1
TDS: INTERRUPT endpoint in with max size 64

For the 3rd insertion call
TDS: prn_parse_descriptors()
TDS: USB_ATTRIB_CONTROL for interface 2
TDS: USB_ATTRIB_INTERRUPT for interface 2
TDS: INTERRUPT endpoint in with max size 64

4th insertion call fails

Tim

first time see control & interrupt with 64 bytes packet;), still think about…
qnx version ?

JIV,

QNX Version 6.3 SP3

Any ideas of what’s wrong with my parsing code or anywhere else I should look in the driver for where I might be doing something wrong?

Tim