diff --git a/usbid/describe.go b/usbid/describe.go index 26f0b22..a01d178 100644 --- a/usbid/describe.go +++ b/usbid/describe.go @@ -6,8 +6,11 @@ import ( "github.com/kylelemons/gousb/usb" ) -// Describe one of the following: -// - *usb.Descriptor Product (Vendor) +// Describe returns a human readable string describing the vendor and product +// of the given device. +// +// The given val must be one of the following: +// - *usb.Descriptor "Product (Vendor)" func Describe(val interface{}) string { switch val := val.(type) { case *usb.Descriptor: @@ -22,8 +25,12 @@ func Describe(val interface{}) string { return fmt.Sprintf("Unknown (%T)", val) } -// Classify one of the following: -// - *usb.Descriptor Class SubClass Protocol +// Classify returns a human-readable string describing the class, subclass, +// and protocol associated with a device or interface. +// +// The given val must be one of the following: +// - *usb.Descriptor "Class (SubClass Protocol" +// - *usb.Interface "IfClass (IfSubClass) IfProtocol" func Classify(val interface{}) string { var class, sub, proto uint8 switch val := val.(type) { diff --git a/usbid/load.go b/usbid/load.go index f7c59d9..b2a0464 100644 --- a/usbid/load.go +++ b/usbid/load.go @@ -9,12 +9,24 @@ import ( ) const ( + // LinuxUsbDotOrg is one source of files in the format used by this package. LinuxUsbDotOrg = "http://www.linux-usb.org/usb.ids" ) -var Vendors map[usb.ID]*Vendor -var Classes map[uint8]*Class +var ( + // Vendors stores the vendor and product ID mappings. + Vendors map[usb.ID]*Vendor + // Classes stores the class, subclass and protocol mappings. + Classes map[uint8]*Class +) + +// LoadFromURL replaces the built-in vendor and class mappings with ones loaded +// from the given URL. +// +// This should usually only be necessary if the mappings in the library are +// stale. The contents of this file as of February 2012 are embedded in the +// library itself. func LoadFromURL(url string) error { resp, err := http.Get(url) if err != nil { diff --git a/usbid/parse.go b/usbid/parse.go index 5083e97..2103cf7 100644 --- a/usbid/parse.go +++ b/usbid/parse.go @@ -10,42 +10,56 @@ import ( "github.com/kylelemons/gousb/usb" ) +// A Vendor contains the name of the vendor and mappings corresponding to all +// known products by their ID. type Vendor struct { Name string Product map[usb.ID]*Product } +// String returns the name of the vendor. func (v Vendor) String() string { return v.Name } +// A Product contains the name of the product (from a particular vendor) and +// the names of any interfaces that were specified. type Product struct { Name string Interface map[usb.ID]string } +// String returns the name of the product. func (p Product) String() string { return p.Name } +// A Class contains the name of the class and mappings for each subclass. type Class struct { Name string SubClass map[uint8]*SubClass } +// String returns the name of the class. func (c Class) String() string { return c.Name } +// A SubClass contains the name of the subclass and any associated protocols. type SubClass struct { Name string Protocol map[uint8]string } +// String returns the name of the SubClass. func (s SubClass) String() string { return s.Name } +// ParseIDs parses and returns mappings from the given reader. In general, this +// should not be necessary, as a set of mappings is already embedded in the library. +// If a new or specialized file is obtained, this can be used to retrieve the mappings, +// which can be stored in the global Vendors and Classes map. func ParseIDs(r io.Reader) (map[usb.ID]*Vendor, map[uint8]*Class, error) { vendors := make(map[usb.ID]*Vendor, 2800) classes := make(map[uint8]*Class) // TODO(kevlar): count diff --git a/usbid/parse_test.go b/usbid/parse_test.go index b786ebf..77b0817 100644 --- a/usbid/parse_test.go +++ b/usbid/parse_test.go @@ -10,8 +10,9 @@ import ( func TestParse(t *testing.T) { tests := []struct { - Input string - Output map[usb.ID]*Vendor + Input string + Vendors map[usb.ID]*Vendor + Classes map[uint8]*Class }{{ Input: ` # Skip comment @@ -43,45 +44,84 @@ C 02 Communications fe Defined by command set descriptor ff Vendor Specific (MSFT RNDIS?) `, - Output: map[usb.ID]*Vendor{ + Vendors: map[usb.ID]*Vendor{ 0xabcd: { Name: "Vendor One", - Devices: map[usb.ID]*Device{ + Product: map[usb.ID]*Product{ 0x0123: {Name: "Product One"}, 0x0124: {Name: "Product Two"}, }, }, 0xefef: { Name: "Vendor Two", - Devices: map[usb.ID]*Device{ + Product: map[usb.ID]*Product{ 0x0aba: { Name: "Product", - Interfaces: map[usb.ID]string{ + Interface: map[usb.ID]string{ 0x12: "Interface One", 0x24: "Interface Two", }, }, 0x0abb: { Name: "Product", - Interfaces: map[usb.ID]string{ + Interface: map[usb.ID]string{ 0x12: "Interface", }, }, }, }, }, + Classes: map[uint8]*Class{ + 0x00: { + Name: "(Defined at Interface level)", + }, + 0x01: { + Name: "Audio", + SubClass: map[uint8]*SubClass{ + 0x01: {Name: "Control Device"}, + 0x02: {Name: "Streaming"}, + 0x03: {Name: "MIDI Streaming"}, + }, + }, + 0x02: { + Name: "Communications", + SubClass: map[uint8]*SubClass{ + 0x01: {Name: "Direct Line"}, + 0x02: { + Name: "Abstract (modem)", + Protocol: map[uint8]string{ + 0x00: "None", + 0x01: "AT-commands (v.25ter)", + 0x02: "AT-commands (PCCA101)", + 0x03: "AT-commands (PCCA101 + wakeup)", + 0x04: "AT-commands (GSM)", + 0x05: "AT-commands (3G)", + 0x06: "AT-commands (CDMA)", + 0xfe: "Defined by command set descriptor", + 0xff: "Vendor Specific (MSFT RNDIS?)", + }, + }, + }, + }, + }, }} for idx, test := range tests { - vendors, err := ParseIDs(strings.NewReader(test.Input)) + vendors, classes, err := ParseIDs(strings.NewReader(test.Input)) if err != nil { t.Errorf("%d. ParseIDs: %s", idx, err) continue } - if got, want := vendors, test.Output; !reflect.DeepEqual(got, want) { - t.Errorf("%d. Parse mismatch", idx) - t.Errorf(" - got: %v", idx, got) - t.Errorf(" - want: %v", idx, want) + if got, want := vendors, test.Vendors; !reflect.DeepEqual(got, want) { + t.Errorf("%d. Vendor parse mismatch", idx) + t.Errorf(" - got: %+v", got) + t.Errorf(" - want: %+v", want) + continue + } + if got, want := classes, test.Classes; !reflect.DeepEqual(got, want) { + t.Errorf("%d. Classes parse mismatch", idx) + t.Errorf(" - got: %+v", got) + t.Errorf(" - want: %+v", want) continue } }