2 Devices with one AVR

General discussions about V-USB, our firmware-only implementation of a low speed USB device on Atmel's AVR microcontrollers
Post Reply
chefpro
Posts: 5
Joined: Sat Nov 11, 2006 5:23 pm
Contact:

2 Devices with one AVR

Post by chefpro » Wed Nov 15, 2006 1:46 am

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.

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Wed Nov 15, 2006 12:38 pm

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.

chefpro
Posts: 5
Joined: Sat Nov 11, 2006 5:23 pm
Contact:

Post by chefpro » Thu Nov 16, 2006 1:15 am

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?

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Thu Nov 16, 2006 9:52 am

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.

os
Posts: 9
Joined: Thu Nov 30, 2006 11:25 pm
Location: Sevastopol, Ukraine
Contact:

Post by os » Thu Nov 30, 2006 11:49 pm

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
};


ksz
Rank 1
Rank 1
Posts: 26
Joined: Tue Jan 09, 2007 9:10 pm
Location: Poland

Post by ksz » Wed Jan 10, 2007 11:37 pm

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?

christian
Objective Development
Objective Development
Posts: 1443
Joined: Thu Nov 09, 2006 11:46 am

Post by christian » Thu Jan 11, 2007 12:10 am

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.

Grendel
Rank 4
Rank 4
Posts: 167
Joined: Sat Dec 16, 2006 9:53 pm
Location: Oregon, USA
Contact:

Post by Grendel » Thu Jan 11, 2007 1:46 am

Can be found here.

os
Posts: 9
Joined: Thu Nov 30, 2006 11:25 pm
Location: Sevastopol, Ukraine
Contact:

Post by os » Thu Jan 11, 2007 9:21 am

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.

Guest

Post by Guest » Thu Jan 11, 2007 5:42 pm

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!

ksz
Rank 1
Rank 1
Posts: 26
Joined: Tue Jan 09, 2007 9:10 pm
Location: Poland

Post by ksz » Thu Jan 11, 2007 5:44 pm

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

Grendel
Rank 4
Rank 4
Posts: 167
Joined: Sat Dec 16, 2006 9:53 pm
Location: Oregon, USA
Contact:

Post by Grendel » Thu Jan 11, 2007 10:52 pm

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.

Post Reply