Files
gousb/usbid/parse.go
2013-08-14 19:22:55 -07:00

246 lines
5.4 KiB
Go

// Copyright 2013 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package usbid
import (
"bufio"
"fmt"
"io"
"strconv"
"strings"
"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
split := func(s string) (kind string, level int, id uint64, name string, err error) {
pieces := strings.SplitN(s, " ", 2)
if len(pieces) != 2 {
err = fmt.Errorf("malformatted line %q", s)
return
}
// Save the name
name = pieces[1]
// Parse out the level
for len(pieces[0]) > 0 && pieces[0][0] == '\t' {
level, pieces[0] = level+1, pieces[0][1:]
}
// Parse the first piece to see if it has a kind
first := strings.SplitN(pieces[0], " ", 2)
if len(first) == 2 {
kind, pieces[0] = first[0], first[1]
}
// Parse the ID
i, err := strconv.ParseUint(pieces[0], 16, 16)
if err != nil {
err = fmt.Errorf("malformatted id %q: %s", pieces[0], err)
return
}
id = i
return
}
// Hold the interim values
var vendor *Vendor
var device *Product
parseVendor := func(level int, raw uint64, name string) error {
id := usb.ID(raw)
switch level {
case 0:
vendor = &Vendor{
Name: name,
}
vendors[id] = vendor
case 1:
if vendor == nil {
return fmt.Errorf("product line without vendor line")
}
device = &Product{
Name: name,
}
if vendor.Product == nil {
vendor.Product = make(map[usb.ID]*Product)
}
vendor.Product[id] = device
case 2:
if device == nil {
return fmt.Errorf("interface line without device line")
}
if device.Interface == nil {
device.Interface = make(map[usb.ID]string)
}
device.Interface[id] = name
default:
return fmt.Errorf("too many levels of nesting for vendor block")
}
return nil
}
// Hold the interim values
var class *Class
var subclass *SubClass
parseClass := func(level int, raw uint64, name string) error {
id := uint8(raw)
switch level {
case 0:
class = &Class{
Name: name,
}
classes[id] = class
case 1:
if class == nil {
return fmt.Errorf("subclass line without class line")
}
subclass = &SubClass{
Name: name,
}
if class.SubClass == nil {
class.SubClass = make(map[uint8]*SubClass)
}
class.SubClass[id] = subclass
case 2:
if subclass == nil {
return fmt.Errorf("protocol line without subclass line")
}
if subclass.Protocol == nil {
subclass.Protocol = make(map[uint8]string)
}
subclass.Protocol[id] = name
default:
return fmt.Errorf("too many levels of nesting for class")
}
return nil
}
// TODO(kevlar): Parse class information, etc
//var class *Class
//var subclass *SubClass
var kind string
lines := bufio.NewReaderSize(r, 512)
parseLines:
for lineno := 0; ; lineno++ {
b, isPrefix, err := lines.ReadLine()
switch {
case err == io.EOF:
break parseLines
case err != nil:
return nil, nil, err
case isPrefix:
return nil, nil, fmt.Errorf("line %d: line too long", lineno)
}
line := string(b)
if len(line) == 0 || line[0] == '#' {
continue
}
k, level, id, name, err := split(line)
if err != nil {
return nil, nil, fmt.Errorf("line %d: %s", lineno, err)
}
if k != "" {
kind = k
}
switch kind {
case "":
err = parseVendor(level, id, name)
case "C":
err = parseClass(level, id, name)
}
if err != nil {
return nil, nil, fmt.Errorf("line %d: %s", lineno, err)
}
}
return vendors, classes, nil
}