diff --git a/usb_linux.go b/usb_linux.go index 8a60a19..35355bb 100644 --- a/usb_linux.go +++ b/usb_linux.go @@ -6,7 +6,102 @@ package serial // import "go.bug.st/serial.v1" +import ( + "bufio" + "fmt" + "os" + "path/filepath" +) + func nativeGetDetailedPortsList() ([]*PortDetails, error) { - // TODO - return nil, &PortError{code: FunctionNotImplemented} + // Retrieve the port list + ports, err := nativeGetPortsList() + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + + var res []*PortDetails + for _, port := range ports { + details, err := nativeGetPortDetails(port) + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + res = append(res, details) + } + return res, nil +} + +func nativeGetPortDetails(portPath string) (*PortDetails, error) { + portName := filepath.Base(portPath) + devicePath := fmt.Sprintf("/sys/class/tty/%s/device", portName) + if _, err := os.Stat(devicePath); err != nil { + return &PortDetails{}, nil + } + realDevicePath, err := filepath.EvalSymlinks(devicePath) + if err != nil { + return nil, fmt.Errorf("Can't determine real path of %s: %s", devicePath, err.Error()) + } + subSystemPath, err := filepath.EvalSymlinks(filepath.Join(realDevicePath, "subsystem")) + if err != nil { + return nil, fmt.Errorf("Can't determine real path of %s: %s", filepath.Join(realDevicePath, "subsystem"), err.Error()) + } + subSystem := filepath.Base(subSystemPath) + + result := &PortDetails{Name: portPath} + switch subSystem { + case "usb-serial": + err := parseUSBSysFS(filepath.Dir(filepath.Dir(realDevicePath)), result) + return result, err + case "usb": + err := parseUSBSysFS(filepath.Dir(realDevicePath), result) + return result, err + // TODO: other cases? + default: + return result, nil + } +} + +func parseUSBSysFS(usbDevicePath string, details *PortDetails) error { + vid, err := readLine(filepath.Join(usbDevicePath, "idVendor")) + if err != nil { + return err + } + pid, err := readLine(filepath.Join(usbDevicePath, "idProduct")) + if err != nil { + return err + } + serial, err := readLine(filepath.Join(usbDevicePath, "serial")) + if err != nil { + return err + } + manufacturer, err := readLine(filepath.Join(usbDevicePath, "manufacturer")) + if err != nil { + return err + } + product, err := readLine(filepath.Join(usbDevicePath, "product")) + if err != nil { + return err + } + + details.IsUSB = true + details.VID = vid + details.PID = pid + details.SerialNumber = serial + details.Manufacturer = manufacturer + details.Product = product + return nil +} + +func readLine(filename string) (string, error) { + file, err := os.Open(filename) + if os.IsNotExist(err) { + return "", nil + } + if err != nil { + return "", err + } + defer file.Close() + reader := bufio.NewReader(file) + line, _, err := reader.ReadLine() + return string(line), err }