Page 1 of 1

2 Devices with one AVR

Posted: Wed Nov 15, 2006 1:46 am
by chefpro
Is there a way to simulate 2 devices with one avr ?

My Problem:

I want to develop a device wich is a gamepad with a led.But I dont want to develop my own gamepad driver.

I want to send the report to switch led from an extern program.
And the gamepad should be used from the normal gameport driver.

In linux I dont find a way to send the report to switch led without unloading the gameport driver.

Is there a way to do so (I know better ask in a linux usb forum) ?

Or, I have to simulate 2 devices.

Posted: Wed Nov 15, 2006 12:38 pm
by christian
You don't need two devices, a composite device with two HID functions should be sufficient. Please see the USB HID specification how to build a report descriptor for a composite device. Or have a look at the report descriptor of a combined keyboard/mouse device.

Posted: Thu Nov 16, 2006 1:15 am
by chefpro
Do someone have an example, how to implement this ?
I was searching the half day, but I cant see where to start.
Is this done with another report descriptor or is there more ?
Until now this is my (not all, borrowed some) code:

Code: Select all


... stuff

char usbHidReportDescriptor[73] = {
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0x09, 0x04,                    // USAGE (Joystick)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x05, 0x02,                    //   USAGE_PAGE (Simulation Controls)
    0x09, 0xbb,                    //   USAGE (Throttle)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
    0x05, 0x09,                    //   USAGE_PAGE (Button)
    0x19, 0x01,                    //   USAGE_MINIMUM (Button 1)
    0x29, 0x0d,                    //   USAGE_MAXIMUM (Button 13)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x0d,                    //   REPORT_COUNT (13)
    0x55, 0x00,                    //   UNIT_EXPONENT (0)
    0x65, 0x00,                    //   UNIT (None)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x03,                    //   REPORT_SIZE (3)
    0x81, 0x01,                    //   INPUT (Cnst,Ary,Abs)
    0xc0,                          // END_COLLECTION
    0x09, 0x01,                    // USAGE (Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x85, 0x01,                    //   REPORT_ID (1)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x09, 0x00,                    //   USAGE (Undefined)
    0xb2, 0x02, 0x01,              //   FEATURE (Data,Var,Abs,Buf)
    0xc0                           // END_COLLECTION
};

... more stuff

uchar   usbFunctionWrite(uchar *data, uchar len)
{
  if( data[0] == 1 ){ // set led
    if( data[1] == 0x00 ){
      resetLED();
    } else {
      setLED();
    }
  }
  return 1;
}

... some stuff

uchar   usbFunctionSetup(uchar data[8])
{
rq = (void *)data;

    usbMsgPtr = reportBuffer;
    if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){    /* class request type */
        if(rq->bRequest == USBRQ_HID_GET_REPORT){  /* wValue: ReportType (highbyte), ReportID (lowbyte) */
            makeNewReport();
            return sizeof(reportBuffer);
        }else if(rq->bRequest == USBRQ_HID_SET_REPORT){  /* wValue: ReportType (highbyte), ReportID (lowbyte) */
            return 0xff;
        }else if(rq->bRequest == USBRQ_HID_GET_IDLE){
            usbMsgPtr = &idleRate;
            return 1;
        }else if(rq->bRequest == USBRQ_HID_SET_IDLE){
            idleRate = rq->wValue.bytes[1];
        }
    }else{
        /* no vendor specific requests implemented */
    }
   return 0;
}

... something

Which works.
So, where start make it stop working ? :D
And make it work again different?

Posted: Thu Nov 16, 2006 9:52 am
by christian
You don't need two HID descriptors, just one with a collection around two functions. I must admit that I never understood the fine details of USB descriptor parsing and always experimented until the operating system accepted the descriptor.

If you can find a composite device (e.g. keyboard with trackball), use that as a starting point.

Posted: Thu Nov 30, 2006 11:49 pm
by os
christian wrote:You don't need two HID descriptors, just one with a collection around two functions.

I tried to implement a USB device with 4 game controllers. They were identical but I think it does not matter. The key is to use two (or more) Application collections with two (or more) different report IDs. The sample HID Report Descriptor may be like this:

Code: Select all

PROGMEM const char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] =
{
    // Controller #1
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x04,                    // USAGE (Joystick)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x85, 0x01,                    //     REPORT_ID (1)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //     LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x0c,                    //     REPORT_COUNT (12)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x0c,                    //     USAGE_MAXIMUM (Button 12)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x95, 0x04,                    //     REPORT_COUNT (4)
    0x81, 0x01,                    //     INPUT (Constant) - 4 bit padding
    0xc0,                          //   END_COLLECTION
    0xc0,                           // END_COLLECTION

    // Controller #2
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x04,                    // USAGE (Joystick)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x85, 0x02,                    //     REPORT_ID (2)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //     LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x0c,                    //     REPORT_COUNT (12)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x0c,                    //     USAGE_MAXIMUM (Button 12)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x95, 0x04,                    //     REPORT_COUNT (4)
    0x81, 0x01,                    //     INPUT (Constant) - 4 bit padding
    0xc0,                          //   END_COLLECTION
    0xc0,                           // END_COLLECTION
};


Posted: Wed Jan 10, 2007 11:37 pm
by ksz
os wrote:
christian wrote:You don't need two HID descriptors, just one with a collection around two functions.

I tried to implement a USB device with 4 game controllers. They were identical but I think it does not matter. The key is to use two (or more) Application collections with two (or more) different report IDs.


And where shoud I add information, which REPORT_ID is comming (I meen in which byte of buffer).. I want to build a mouse which I want to control by IR remote and by buttons on it.. Buttons part I've already done, it's got standard mouse descriptor:

Code: Select all

    
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x02,                    // USAGE (Mouse)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
                                        // I guess here goes the REPORT_ID
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x75, 0x05,                    //     REPORT_SIZE (5)
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
    0xc0,                          //   END_COLLECTION
    0xc0                           // END_COLLECTION


The second descriptor will be the same..
But how in buildReport function add which REPORT_ID I want use..?
More info about my device:
buttons are checked every 22us, if changed - buildReportButtons() updates reportBufferButtons[] and sends to host..
IR transmition will be detected in interrupts, after receive full IR frame, it will be prepared to send in buildReportIR() and send..
How in this functions add select e.g. REPORT_ID(1) for buttons, 2 - IR?

Posted: Thu Jan 11, 2007 12:10 am
by christian
Did you know that the USB Implementers Forum (http://www.usb.org) offers an application which creates HID descriptors? I can't find it right now, but it must be somewhere in the Downloads area of the Developer section.

Posted: Thu Jan 11, 2007 1:46 am
by Grendel
Can be found here.

Posted: Thu Jan 11, 2007 9:21 am
by os
ksz wrote:And where shoud I add information, which REPORT_ID is comming (I meen in which byte of buffer)..

ReportID byte is the first byte of the buffer if you use it in the HID Report Descriptor.

You may see my implementation of RC USB controller (downloadable from ObDev web site). out_joystick_btn.c implements two reports for HID-compatible joystick with buttons. One report is for axes, the second - for buttons.

If host requests a specific ReportID then it is checked in usbFunctionSetup(). If it was an USB Interrupt In transfer then HID reports are sent by turns. In that case the firmware keeps track of which ReportID is to be sent this time (and you may use different repeat intervals for them if you wish, e.g. send the reports as 1, 1, 1, 2, 1, 1, 1, 2, ...). Note that WinXP usually uses Interrupt transfer, and PC application simply reads data from WinXP buffers (a lot of them by default, up to 512 buffers software controllable, 32 (?) by default).

Hope this helps.

Posted: Thu Jan 11, 2007 5:42 pm
by Guest
christian wrote:Did you know that the USB Implementers Forum (http://www.usb.org) offers an application which creates HID descriptors? I can't find it right now, but it must be somewhere in the Downloads area of the Developer section.


Yes, I've used this to create my first mouse report..
What I didn't know is what wrote os:
[quote="os"]
ReportID byte is the first byte of the buffer if you use it in the HID Report Descriptor. ...........[quote]
Yes, os, this helps, thanks a lot!

Posted: Thu Jan 11, 2007 5:44 pm
by ksz
Anonymous wrote:Yes, I've used this to create my first mouse report..
What I didn't know is what wrote os:

"ReportID byte is the first byte of the buffer if you use it in the HID Report Descriptor. ........... "

Yes, os, this helps, thanks a lot!


Sorry, not logged in, this was me - ksz

Posted: Thu Jan 11, 2007 10:52 pm
by Grendel
This is all outlined in Device Class Definition for HID 1.11 and HID Usage Tables 1.12 found on usb.org's HID Information page.. Very good reads (hint, hint :P.)

Edit: Whatdoyouknow -- there's a standard controller for magic carpets..

HID Usage Tables 1.12 wrote:5.7 Miscellaneous Simulation Devices

Magic Carpet Simulation Device

CA – Allows a device to be generally classified as one that uses the standard control of a magic carpet. This control is a bar, grasped by both hands, that controls the Yaw, Pitch and Roll of the carpet.

The bar, at which the pilot sits, may be pushed forward or pulled back to cause the carpet to dive or rise, respectively. In the zero position, the carpet is in level flight. Pushing forward on the bar causes the carpet to nose down and generates negative values. Pulling back on the bar causes the carpet to nose up and generates positive values.

Turning the bar turns the carpet. In the zero position, the carpet travels straight ahead. Pulling back on the right side turns the carpet to the right and generates positive values. Pulling back on the left side turns the carpet to the left and generates negative values.

Rotating the bar rolls the carpet. In the zero position, the carpet travels level. Rotating the bar in a clockwise direction rolls the carpet to the right and generates positive values. Rotating the bar in the counterclockwise direction rolls the carpet to the left and generates negative values.