Arduino leonardoはUSB機器としてちょっと特殊だ、という話
ひとつの機器に複数の機能が付いてるようなUSB機器には、実は異なる構成があり、一例としてArduino leonardoはちょっと特殊だ、という話です。
USB compound device
まず、論理的にはハブにぶら下がってるようなのがcompound deviceですが、これはOS側から問題になるようなことはないでしょうので省略します。
USB composite device
これはひとつのコンフィギュレーションの下に、複数のインタフェースがある、というような機器で、よく見られるようなものではないかと思います。たとえば手元のマウスG9がこの方式で、マウスだけでなく同時にキーボードとしても機能します。手元の環境でコンフィグ情報を見ると次のようになります。
$ sudo usbconfig -d 0.2 dump_curr_config_desc ugen0.2: <G9 Laser Mouse Logitech> at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (98mA) Configuration index 0 bLength = 0x0009 bDescriptorType = 0x0002 wTotalLength = 0x003b bNumInterfaces = 0x0002 bConfigurationValue = 0x0001 iConfiguration = 0x0004 <U50.00_B0029> bmAttributes = 0x00a0 bMaxPower = 0x0031 Interface 0 bLength = 0x0009 bDescriptorType = 0x0004 bInterfaceNumber = 0x0000 bAlternateSetting = 0x0000 bNumEndpoints = 0x0001 bInterfaceClass = 0x0003 ← クラス = 3(HID) bInterfaceSubClass = 0x0001 ← サブクラス = 1(ブートインタフェース) bInterfaceProtocol = 0x0002 ← プロトコル = 2(マウス) iInterface = 0x0000 <no string> Additional Descriptor bLength = 0x09 bDescriptorType = 0x21 bDescriptorSubType = 0x11 RAW dump: 0x00 | 0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x43, 0x08 | 0x00 Endpoint 0 bLength = 0x0007 bDescriptorType = 0x0005 bEndpointAddress = 0x0081 <IN> bmAttributes = 0x0003 <INTERRUPT> wMaxPacketSize = 0x0008 bInterval = 0x0001 bRefresh = 0x0000 bSynchAddress = 0x0000 Interface 1 bLength = 0x0009 bDescriptorType = 0x0004 bInterfaceNumber = 0x0001 bAlternateSetting = 0x0000 bNumEndpoints = 0x0001 bInterfaceClass = 0x0003 bInterfaceSubClass = 0x0000 bInterfaceProtocol = 0x0000 iInterface = 0x0000 <no string> Additional Descriptor bLength = 0x09 bDescriptorType = 0x21 bDescriptorSubType = 0x11 RAW dump: 0x00 | 0x09, 0x21, 0x11, 0x01, 0x00, 0x01, 0x22, 0x7a, 0x08 | 0x00 Endpoint 0 bLength = 0x0007 bDescriptorType = 0x0005 bEndpointAddress = 0x0082 <IN> bmAttributes = 0x0003 <INTERRUPT> wMaxPacketSize = 0x0014 bInterval = 0x0001 bRefresh = 0x0000 bSynchAddress = 0x0000
書き加えてあるように、インタフェース0はブートインタフェースとしてマウスが見えています。インタフェース1はブートインタフェースでないため(TBD:ブートインタフェースでないデバイスの識別方法)ですが、キーボードが見えています。
compound deviceはこのように、機能の数だけインタフェースがあるので、OS側ではインタフェース毎にデバドラが対応すれば良いということになりますが、そのようになってないデバイスもあります。
Multiple Top-Level
どうも一般名称がまだ定まってない感じなのですが、HID機器などで、単一のインタフェースの下に複数の機能がぶら下がっているものがあり、手元の環境である FreeBSD では、そういったHID機器に対応するためには、ベースシステムに入っていないuhiddというデーモンが必要になります( https://wiki.freebsd.org/uhidd )。
Arduino leonardoが、ホストに対してキーボードやマウスをエミュレーションする機能を持っていますが、そのキーボードやマウスがこの方式でした。以下に、コンフィグ情報を示します。
$ sudo usbconfig -d 0.4 dump_curr_config_desc ugen0.4: <Arduino Leonardo Arduino LLC> at usbus0, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (500mA) Configuration index 0 bLength = 0x0009 bDescriptorType = 0x0002 wTotalLength = 0x0064 bNumInterfaces = 0x0003 bConfigurationValue = 0x0001 iConfiguration = 0x0000 <no string> bmAttributes = 0x0080 bMaxPower = 0x00fa Additional Descriptor bLength = 0x08 bDescriptorType = 0x0b bDescriptorSubType = 0x00 RAW dump: 0x00 | 0x08, 0x0b, 0x00, 0x02, 0x02, 0x02, 0x01, 0x00 Interface 0 bLength = 0x0009 bDescriptorType = 0x0004 bInterfaceNumber = 0x0000 bAlternateSetting = 0x0000 bNumEndpoints = 0x0001 bInterfaceClass = 0x0002 ← クラス = 2(CDC) bInterfaceSubClass = 0x0002 bInterfaceProtocol = 0x0000 iInterface = 0x0000 <no string> Additional Descriptor bLength = 0x05 bDescriptorType = 0x24 bDescriptorSubType = 0x00 RAW dump: 0x00 | 0x05, 0x24, 0x00, 0x10, 0x01 Additional Descriptor bLength = 0x05 bDescriptorType = 0x24 bDescriptorSubType = 0x01 RAW dump: 0x00 | 0x05, 0x24, 0x01, 0x01, 0x01 Additional Descriptor bLength = 0x04 bDescriptorType = 0x24 bDescriptorSubType = 0x02 RAW dump: 0x00 | 0x04, 0x24, 0x02, 0x06 Additional Descriptor bLength = 0x05 bDescriptorType = 0x24 bDescriptorSubType = 0x06 RAW dump: 0x00 | 0x05, 0x24, 0x06, 0x00, 0x01 Endpoint 0 bLength = 0x0007 bDescriptorType = 0x0005 bEndpointAddress = 0x0081 <IN> bmAttributes = 0x0003 <INTERRUPT> wMaxPacketSize = 0x0010 bInterval = 0x0040 bRefresh = 0x0000 bSynchAddress = 0x0000 Interface 1 bLength = 0x0009 bDescriptorType = 0x0004 bInterfaceNumber = 0x0001 bAlternateSetting = 0x0000 bNumEndpoints = 0x0002 bInterfaceClass = 0x000a ← クラス = 0xa(CDC-Data) bInterfaceSubClass = 0x0000 bInterfaceProtocol = 0x0000 iInterface = 0x0000 <no string> Endpoint 0 bLength = 0x0007 bDescriptorType = 0x0005 bEndpointAddress = 0x0002 <OUT> bmAttributes = 0x0002 <BULK> wMaxPacketSize = 0x0040 bInterval = 0x0000 bRefresh = 0x0000 bSynchAddress = 0x0000 Endpoint 1 bLength = 0x0007 bDescriptorType = 0x0005 bEndpointAddress = 0x0083 <IN> bmAttributes = 0x0002 <BULK> wMaxPacketSize = 0x0040 bInterval = 0x0000 bRefresh = 0x0000 bSynchAddress = 0x0000 Interface 2 bLength = 0x0009 bDescriptorType = 0x0004 bInterfaceNumber = 0x0002 bAlternateSetting = 0x0000 bNumEndpoints = 0x0001 bInterfaceClass = 0x0003 ← クラス = 3(HID) bInterfaceSubClass = 0x0000 bInterfaceProtocol = 0x0000 iInterface = 0x0000 <no string> Additional Descriptor bLength = 0x09 bDescriptorType = 0x21 bDescriptorSubType = 0x01 RAW dump: 0x00 | 0x09, 0x21, 0x01, 0x01, 0x00, 0x01, 0x22, 0x65, 0x08 | 0x00 Endpoint 0 bLength = 0x0007 bDescriptorType = 0x0005 bEndpointAddress = 0x0084 <IN> bmAttributes = 0x0003 <INTERRUPT> wMaxPacketSize = 0x0040 bInterval = 0x0001 bRefresh = 0x0000 bSynchAddress = 0x0000
書き加えてあるように、インタフェースは3個ありますが、クラスがHIDなのは1個だけです。Arduino leonardoのキーボードとマウスのエミュレーション機能は、この1個のHIDインタフェースに両方がぶら下がっています。なので、FreeBSDでArduino leonardoのこの機能を使うには、前述のuhiddが必要になります。