Forum: Mikrocontroller und Digitale Elektronik Atmega32U2, LUFA, mehrere HID Geräte


von Artur H. (hohoho)


Lesenswert?

Hallo,

ich mache gerade meine ersten Schritte mit USB mithilfe eines 
ATmega32U2.
Was ich vorhabe, ist ein Gerät, welches sowohl als MIDI Device ( HID ) 
als auch als Generic HID Device ( für normale Steuerbefehle ) angemeldet 
wird.

Mithilfe der Beispiele von LUFA ist mir auch entweder das eine oder das 
andere gelungen, aber dank USB sollte es doch möglich sein, beides auf 
einem USB-Gerät zu verbinden ( 2 Endpoints für MIDI, 2 Endpoints für 
Generic HID ).

Leider fehlt mir ein vernünftiger Ansatz, das zu schaffen, es scheitert 
an den Descriptors, insbesondere dem ConfigurationDescriptor.

Der ist bei beiden Geräten anders definiert...
für MIDI:
1
typedef struct
2
    {
3
      USB_Descriptor_Configuration_Header_t Config;
4
      USB_Descriptor_Interface_t            Audio_ControlInterface;
5
      USB_Audio_Interface_AC_t              Audio_ControlInterface_SPC;
6
      USB_Descriptor_Interface_t            Audio_StreamInterface;
7
      USB_Audio_Interface_MIDI_AS_t         Audio_StreamInterface_SPC;
8
      USB_MIDI_In_Jack_t                    MIDI_In_Jack_Emb;
9
      USB_MIDI_In_Jack_t                    MIDI_In_Jack_Ext;
10
      USB_MIDI_Out_Jack_t                   MIDI_Out_Jack_Emb;
11
      USB_MIDI_Out_Jack_t                   MIDI_Out_Jack_Ext;
12
      USB_Audio_StreamEndpoint_Std_t        MIDI_In_Jack_Endpoint;
13
      USB_MIDI_Jack_Endpoint_t              MIDI_In_Jack_Endpoint_SPC;
14
      USB_Audio_StreamEndpoint_Std_t        MIDI_Out_Jack_Endpoint;
15
      USB_MIDI_Jack_Endpoint_t              MIDI_Out_Jack_Endpoint_SPC;
16
    } USB_Descriptor_Configuration_t;

Für Generic HID:
1
typedef struct
2
    {
3
      USB_Descriptor_Configuration_Header_t Config;
4
      USB_Descriptor_Interface_t            HID_Interface;
5
      USB_Descriptor_HID_t                  HID_GenericHID;
6
          USB_Descriptor_Endpoint_t             HID_ReportINEndpoint;
7
          USB_Descriptor_Endpoint_t             HID_ReportOUTEndpoint;
8
    } USB_Descriptor_Configuration_t;

Die frage ist nun, wie bringe ich das unter einen Hut ?
Einfach beides zusammenkopieren hat nicht geholfen, dann übersteht das 
Gerät nicht mal die Enumeration-Phase.

Hätte jemand einen Tipp, wie man das angeht ?

Gruß,
Artur

von René K. (king)


Lesenswert?

Du hast für mehrere Interfaces nur eine Configuration. Also eher so:
1
typedef struct
2
    {
3
      USB_Descriptor_Configuration_Header_t Config;
4
      USB_Descriptor_Interface_t            Audio_ControlInterface;
5
      USB_Audio_Interface_AC_t              Audio_ControlInterface_SPC;
6
      USB_Descriptor_Interface_t            Audio_StreamInterface;
7
      USB_Audio_Interface_MIDI_AS_t         Audio_StreamInterface_SPC;
8
      USB_MIDI_In_Jack_t                    MIDI_In_Jack_Emb;
9
      USB_MIDI_In_Jack_t                    MIDI_In_Jack_Ext;
10
      USB_MIDI_Out_Jack_t                   MIDI_Out_Jack_Emb;
11
      USB_MIDI_Out_Jack_t                   MIDI_Out_Jack_Ext;
12
      USB_Audio_StreamEndpoint_Std_t        MIDI_In_Jack_Endpoint;
13
      USB_MIDI_Jack_Endpoint_t              MIDI_In_Jack_Endpoint_SPC;
14
      USB_Audio_StreamEndpoint_Std_t        MIDI_Out_Jack_Endpoint;
15
      USB_MIDI_Jack_Endpoint_t              MIDI_Out_Jack_Endpoint_SPC;
16
17
      USB_Descriptor_Interface_t            HID_Interface;
18
      USB_Descriptor_HID_t                  HID_GenericHID;
19
          USB_Descriptor_Endpoint_t             HID_ReportINEndpoint;
20
          USB_Descriptor_Endpoint_t             HID_ReportOUTEndpoint;
21
    } USB_Descriptor_Configuration_t;

Im Configuration-Descriptor setzt Du nun aber bNumInterfaces auf 3 und 
sorgst dafür, das die drei Interface-Descriptoren unterschiedliche Werte 
für bInterfaceNumber aufweisen.

Da sowohl MIDI als auch Audio nicht HID ist und Du bereits für Audio 
mehrere Interface-Descriptoren hast, die logisch zusammen gehören, wirst 
Du wohl auch um den Interface Association Descriptor nicht herumkommen 
(wie ihn die Version 2 der Audio Device Class Definition sogar 
vorschreibt). Siehe: 
http://www.usb.org/developers/whitepapers/iadclasscode_r10.pdf

von Artur H. (hohoho)


Lesenswert?

Vielen Dank schonmal für diese Informationen ...
also habe ich das richtig verstanden ... die AudioControl und 
AudioStream Interfaces in einer IAD und das GenericHID Interface in 
einer anderen IAD zusammenfassen ?

Das würde dann so aussehen ( diesmal nicht die Typdefinition, sondern 
die konkreten Werte ):
1
USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor =
2
{
3
  .Config = 
4
    {
5
      .Header                   = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration},
6
7
      .TotalConfigurationSize   = sizeof(USB_Descriptor_Configuration_t),
8
      .TotalInterfaces          = 3,
9
10
      .ConfigurationNumber      = 1,
11
      .ConfigurationStrIndex    = NO_DESCRIPTOR,
12
        
13
      .ConfigAttributes         = (USB_CONFIG_ATTR_BUSPOWERED | USB_CONFIG_ATTR_SELFPOWERED),
14
      
15
      .MaxPowerConsumption      = USB_CONFIG_POWER_MA(100)
16
    },
17
18
  .Audio_ControlInterface_IAD =
19
  {
20
    .Header                 = {.Size = sizeof(USB_Descriptor_Interface_Association_t), .Type = DTYPE_InterfaceAssociation},
21
22
    .FirstInterfaceIndex    = 0,
23
    .TotalInterfaces        = 2,
24
25
    .Class                  = 0x01,
26
    .SubClass               = 0x03,
27
    .Protocol               = 0x00,
28
29
    .IADStrIndex            = NO_DESCRIPTOR
30
  },
31
    
32
  .Audio_ControlInterface = 
33
    {
34
      .Header                   = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},
35
36
      .InterfaceNumber          = 0,
37
      .AlternateSetting         = 0,
38
      
39
      .TotalEndpoints           = 0,
40
        
41
      .Class                    = 0x01,
42
      .SubClass                 = 0x01,
43
      .Protocol                 = 0x00,
44
        
45
      .InterfaceStrIndex        = NO_DESCRIPTOR      
46
    },
47
  
48
  .Audio_ControlInterface_SPC = 
49
    {
50
      .Header                   = {.Size = sizeof(USB_Audio_Interface_AC_t), .Type = DTYPE_AudioInterface},
51
      .Subtype                  = DSUBTYPE_Header,
52
      
53
      .ACSpecification          = VERSION_BCD(01.00),
54
      .TotalLength              = sizeof(USB_Audio_Interface_AC_t),
55
      
56
      .InCollection             = 1,
57
      .InterfaceNumbers         = {1},      
58
    },
59
60
  .Audio_StreamInterface = 
61
    {
62
      .Header                   = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},
63
64
      .InterfaceNumber          = 1,
65
      .AlternateSetting         = 0,
66
      
67
      .TotalEndpoints           = 2,
68
        
69
      .Class                    = 0x01,
70
      .SubClass                 = 0x03,
71
      .Protocol                 = 0x00,
72
        
73
      .InterfaceStrIndex        = NO_DESCRIPTOR
74
    },
75
    
76
  .Audio_StreamInterface_SPC = 
77
    {
78
      .Header                   = {.Size = sizeof(USB_Audio_Interface_MIDI_AS_t), .Type = DTYPE_AudioInterface},
79
      .Subtype                  = DSUBTYPE_General,
80
81
      .AudioSpecification       = VERSION_BCD(01.00),
82
      
83
      .TotalLength              = (sizeof(USB_Descriptor_Configuration_t) -
84
                                   offsetof(USB_Descriptor_Configuration_t, Audio_StreamInterface_SPC))
85
    },
86
87
  .HID_IAD =
88
  {
89
    .Header                 = {.Size = sizeof(USB_Descriptor_Interface_Association_t), .Type = DTYPE_InterfaceAssociation},
90
91
    .FirstInterfaceIndex    = 2,
92
    .TotalInterfaces        = 1,
93
94
    .Class                  = 0x03,
95
    .SubClass               = 0x00,
96
    .Protocol               = 0x00,
97
98
    .IADStrIndex            = NO_DESCRIPTOR
99
  },
100
101
  .HID_Interface = 
102
    {
103
      .Header                 = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface},
104
105
      .InterfaceNumber        = 0x02,
106
      .AlternateSetting       = 0x00,
107
      
108
      .TotalEndpoints         = 2,
109
        
110
      .Class                  = 0x03,
111
      .SubClass               = 0x00,
112
      .Protocol               = 0x00,
113
        
114
      .InterfaceStrIndex      = NO_DESCRIPTOR
115
    },
116
117
  .HID_GenericHID = 
118
    {
119
      .Header                 = {.Size = sizeof(USB_Descriptor_HID_t), .Type = DTYPE_HID},
120
                   
121
      .HIDSpec                = VERSION_BCD(01.11),
122
      .CountryCode            = 0x00,
123
      .TotalReportDescriptors = 1,
124
      .HIDReportType          = DTYPE_Report,
125
      .HIDReportLength        = sizeof(GenericReport)
126
    },
127
128
  .HID_ReportINEndpoint = 
129
    {
130
      .Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},
131
                     
132
      .EndpointAddress        = (ENDPOINT_DESCRIPTOR_DIR_IN | GENERIC_IN_EPNUM),
133
      .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
134
      .EndpointSize           = GENERIC_EPSIZE,
135
      .PollingIntervalMS      = 0x0A
136
    },
137
138
  .HID_ReportOUTEndpoint = 
139
    {
140
      .Header                 = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint},
141
                     
142
      .EndpointAddress        = (ENDPOINT_DESCRIPTOR_DIR_OUT | GENERIC_OUT_EPNUM),
143
      .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),
144
      .EndpointSize           = GENERIC_EPSIZE,
145
      .PollingIntervalMS      = 0x0A
146
    },
147
148
... die Midi Jack Descriptoren habe ich alle aus der LUFA Demo kopiert, daher lass ich sie hier mal raus
149
};
funktioniert leider immer noch nicht ... beim Anstecken sucht Windows 
die Treiber, findet sie aber nicht und trägt das Gerät einfach nur als 
"USB Composite Device" ein, mit der Info, dass das Gerät nicht gestartet 
werden konnte.

von René K. (king)


Lesenswert?

Artur H. schrieb:
> Vielen Dank schonmal für diese Informationen ...
> also habe ich das richtig verstanden ... die AudioControl und
> AudioStream Interfaces in einer IAD und das GenericHID Interface in
> einer anderen IAD zusammenfassen ?

Das HID würde ich einfach folgen lassen. Es gibt hierfür ja nur ein 
Interface, da brauchst Du nichts zusammenzufassen.

Für den IAD musst Du den Device Descriptor aber auch anpassen, um 
anzuzeigen, dass Du eben den IAD verwenden willst (Tabelle 1-1 aus dem 
oben verlinkten PDF). Hast Du das beachtet?

> funktioniert leider immer noch nicht ... beim Anstecken sucht Windows
> die Treiber, findet sie aber nicht und trägt das Gerät einfach nur als
> "USB Composite Device" ein, mit der Info, dass das Gerät nicht gestartet
> werden konnte.

Welches Gerät? Sind beide betroffen, oder wird zumindest eines 
verwendet?

von Artur H. (hohoho)


Lesenswert?

René König schrieb:
> Das HID würde ich einfach folgen lassen. Es gibt hierfür ja nur ein
> Interface, da brauchst Du nichts zusammenzufassen.
Ok, habe ich mal gemacht.

> Für den IAD musst Du den Device Descriptor aber auch anpassen, um
> anzuzeigen, dass Du eben den IAD verwenden willst (Tabelle 1-1 aus dem
> oben verlinkten PDF). Hast Du das beachtet?
Jawohl, funktioniert immer noch nicht.

> Welches Gerät? Sind beide betroffen, oder wird zumindest eines
> verwendet?
Beide sind betroffen, keins funktioniert.
Wenn ich jedoch den kompletten Generic-HID-Teil rauskommentiere, 
funktioniert zumindest das MIDI-Device ohne Probleme ... mein Ziel ist 
aber, beides zusammen hinzukriegen.

von René K. (king)


Lesenswert?

Artur H. schrieb:
1
>       .EndpointSize           = GENERIC_EPSIZE,
2
>       .PollingIntervalMS      = 0x0A
3
>     },
4
> 
5
> ... die Midi Jack Descriptoren habe ich alle aus der LUFA Demo kopiert,
6
> daher lass ich sie hier mal raus
7
> };
8
>

Das sehe ich ja jetzt erst: Wenn Du sie tatsächlich hier rausgelassen 
hast, funktioniert das auch nicht. Ich hoffe, Du hast sie weiter oben, 
also bereits vor dem HID_Interface, rausgelassen.

Ansonsten: Funktioniert das HID allein? Wenn ja, hast Du vielleicht 
Endpoint-Adressen doppelt vergeben? HID IN und HID OUT darf sich nicht 
mit Deinen MIDI Jacks beißen.

Hast Du Dir das mal mit einem Protokoll-Analyzer angesehen? Bis wohin 
läuft denn die Enumeration?

von Artur H. (hohoho)


Lesenswert?

René König schrieb:
> Das sehe ich ja jetzt erst: Wenn Du sie tatsächlich hier rausgelassen
> hast, funktioniert das auch nicht. Ich hoffe, Du hast sie weiter oben,
> also bereits vor dem HID_Interface, rausgelassen.
In der Tat, das hat nun einiges geändert ... beim Anstöpseln will er nun 
insgesamt 3 Treiber installieren ...
1) USB Composite Device, den Treiber findet er offenbar auch
2) USB Audio Device, den Treiber findet er nicht
3) USB Input Device ( also das Generic HID ), den findet er auch
Generic HID funktioniert nun, aber MIDI klappt nicht.

Endpoint Adressen habe ich 1(IN), 2(OUT) für MIDI und 3(IN), 4(OUT) für 
Generic HID, das sollte also kein Problem geben.

> Hast Du Dir das mal mit einem Protokoll-Analyzer angesehen? Bis wohin
> läuft denn die Enumeration?
Noch nicht, werd mich gleich mal auf die Suche nach solchen Programmen 
machen.

Nebenbei bemerkt: Vielen Dank für deine Geduld, René :)

von René K. (king)


Lesenswert?

Hmh. Also wenn MIDI inklusive IAD ohne das HID funktioniert, sollte es 
auch zusammen mit dem HID funktionieren. Ein Schuß ins Blaue: Stimmt die 
Kalkulation von Audio_StreamInterface_SPC.TotalLength, oder kommt da 
zusammen mit dem HID ein anderer Wert heraus?

von Artur H. (hohoho)


Lesenswert?

René König schrieb:
> Hmh. Also wenn MIDI inklusive IAD ohne das HID funktioniert, sollte es
> auch zusammen mit dem HID funktionieren. Ein Schuß ins Blaue: Stimmt die
> Kalkulation von Audio_StreamInterface_SPC.TotalLength, oder kommt da
> zusammen mit dem HID ein anderer Wert heraus?
Wow, volltreffer!
Ich habe die Länge der HID-Structs raussubtrahiert, und nun geht beides.
Vielen Dank!

von Artur H. (hohoho)


Lesenswert?

Edit: Hat sich erledigt, hatte ein break an der falschen stelle gelöscht

von Kaspar (Gast)


Lesenswert?

Entschuldigt das ich diesen alten Thread wieder ausgrabe aber ich habe 
ein sehr aehnliches problem. Ich versuche ein CDC + MIDI geraet ueber 
IAD anzumelden. An sich klappt das auch aber irgendwo laueft doch was 
schief und die CDC kommunikation bricht ab.

Bis jetzt hatte ich die Audio_StreamInterface_SPC.TotalLength noch nicht 
veraendert. Wie definiere ich denn die Laenge meines CDC-Structs? Wie 
wurde denn die laenge des HID-Structs konkret im Kode definiert und 
subtrahiert? Macht es in welcher reihenvolge ich die CDC-Endpoints und 
die MIDI-Endpoints habe?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.