Moved USB enumerations subroutines in their own package

This commit is contained in:
Cristian Maglie
2017-01-07 23:38:51 +01:00
parent 9d2bfefb78
commit c8b1c23a56
13 changed files with 202 additions and 152 deletions

19
doc.go
View File

@@ -79,12 +79,14 @@ serial port:
fmt.Printf("%v", string(buff[:n]))
}
If a port is a virutal USB-CDC serial port (an USB-to-RS232 cable or a
microcontroller development board are typical examples) is possible to
retrieve the USB metadata, like VID/PID or USB Serial Number, with the
GetDetailedPortsList function:
If a port is a virtual USB-CDC serial port (for example an USB-to-RS232
cable or a microcontroller development board) is possible to retrieve
the USB metadata, like VID/PID or USB Serial Number, with the
GetDetailedPortsList function in the enumerator package:
ports, err := serial.GetDetailedPortsList()
import "go.bug.st/serial.v1/enumerator"
ports, err := enumerator.GetDetailedPortsList()
if err != nil {
log.Fatal(err)
}
@@ -100,5 +102,12 @@ GetDetailedPortsList function:
}
}
for details on USB port enumeration see the documentation of the specific package.
This library tries to avoid the use of the "C" package (and consequently the need
of cgo) to simplify cross compiling.
Unfortunately the USB enumeration package for darwin (MacOSX) requires cgo
to access the IOKit framework. This means that if you need USB enumeration
on darwin you're forced to use cgo.
*/
package serial // import "go.bug.st/serial.v1"

41
enumerator/enumerator.go Normal file
View File

@@ -0,0 +1,41 @@
//
// Copyright 2014-2017 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
package enumerator // import "go.bug.st/serial.v1/enumerator"
// PortDetails contains detailed information about USB serial port.
// Use GetDetailedPortsList function to retrieve it.
type PortDetails struct {
Name string
IsUSB bool
VID string
PID string
SerialNumber string
// Manufacturer string
// Product string
}
// GetDetailedPortsList retrieve ports details like USB VID/PID.
// Please note that this function may not be available on all OS:
// in that case a FunctionNotImplemented error is returned.
func GetDetailedPortsList() ([]*PortDetails, error) {
return nativeGetDetailedPortsList()
}
// PortEnumerationError is the error type for serial ports enumeration
type PortEnumerationError struct {
causedBy error
}
// Error returns the complete error code with details on the cause of the error
func (e PortEnumerationError) Error() string {
reason := "Error while enumerating serial ports"
if e.causedBy != nil {
reason += ": " + e.causedBy.Error()
}
return reason
}

View File

@@ -1,17 +1,17 @@
//
// Copyright 2014-2016 Cristian Maglie. All rights reserved.
// Copyright 2014-2017 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
package serial_test
package enumerator_test
import "fmt"
import "log"
import "go.bug.st/serial.v1"
import "go.bug.st/serial.v1/enumerator"
func ExampleGetDetailedPortsList() {
ports, err := serial.GetDetailedPortsList()
ports, err := enumerator.GetDetailedPortsList()
if err != nil {
log.Fatal(err)
}

View File

@@ -0,0 +1,113 @@
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
package enumerator
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var _ unsafe.Pointer
var (
modsetupapi = windows.NewLazySystemDLL("setupapi.dll")
procSetupDiClassGuidsFromNameW = modsetupapi.NewProc("SetupDiClassGuidsFromNameW")
procSetupDiGetClassDevsW = modsetupapi.NewProc("SetupDiGetClassDevsW")
procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList")
procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo")
procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW")
procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey")
procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW")
)
func setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(class)
if err != nil {
return
}
return _setupDiClassGuidsFromNameInternal(_p0, guid, guidSize, requiredSize)
}
func _setupDiClassGuidsFromNameInternal(class *uint16, guid *guid, guidSize uint32, requiredSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameW.Addr(), 4, uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(guid)), uintptr(guidSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) {
r0, _, e1 := syscall.Syscall6(procSetupDiGetClassDevsW.Addr(), 4, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(enumerator)), uintptr(hwndParent), uintptr(flags), 0, 0)
set = devicesSet(r0)
if set == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiDestroyDeviceInfoList(set devicesSet) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(set), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiEnumDeviceInfo(set devicesSet, index uint32, info *devInfoData) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(set), uintptr(index), uintptr(unsafe.Pointer(info)))
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetDeviceInstanceId(set devicesSet, devInfo *devInfoData, devInstanceId unsafe.Pointer, devInstanceIdSize uint32, requiredSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiGetDeviceInstanceIdW.Addr(), 5, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(devInstanceId), uintptr(devInstanceIdSize), uintptr(unsafe.Pointer(requiredSize)), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall6(procSetupDiOpenDevRegKey.Addr(), 6, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(scope), uintptr(hwProfile), uintptr(keyType), uintptr(samDesired))
hkey = syscall.Handle(r0)
if hkey == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, outSize *uint32, reqSize *uint32) (res bool) {
r0, _, _ := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(property), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(outValue)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(reqSize)), 0, 0)
res = r0 != 0
return
}

View File

@@ -1,10 +1,10 @@
//
// Copyright 2014-2016 Cristian Maglie. All rights reserved.
// Copyright 2014-2017 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
package serial // import "go.bug.st/serial.v1"
package enumerator // import "go.bug.st/serial.v1/enumerator"
// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -fconstant-cfstrings
// #include <IOKit/IOKitLib.h>

View File

@@ -1,12 +1,12 @@
//
// Copyright 2014-2016 Cristian Maglie. All rights reserved.
// Copyright 2014-2017 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
package serial // import "go.bug.st/serial.v1"
package enumerator // import "go.bug.st/serial.v1/enumerator"
func nativeGetDetailedPortsList() ([]*PortDetails, error) {
// TODO
return nil, &PortError{code: FunctionNotImplemented}
return nil, &PortEnumerationError{}
}

View File

@@ -1,30 +1,32 @@
//
// Copyright 2014-2016 Cristian Maglie. All rights reserved.
// Copyright 2014-2017 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
package serial // import "go.bug.st/serial.v1"
package enumerator // import "go.bug.st/serial.v1/enumerator"
import (
"bufio"
"fmt"
"os"
"path/filepath"
"go.bug.st/serial.v1"
)
func nativeGetDetailedPortsList() ([]*PortDetails, error) {
// Retrieve the port list
ports, err := nativeGetPortsList()
ports, err := serial.GetPortsList()
if err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err}
return nil, &PortEnumerationError{causedBy: err}
}
var res []*PortDetails
for _, port := range ports {
details, err := nativeGetPortDetails(port)
if err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err}
return nil, &PortEnumerationError{causedBy: err}
}
res = append(res, details)
}

View File

@@ -1,12 +1,12 @@
//
// Copyright 2014-2016 Lars Knudsen, Cristian Maglie. All rights reserved.
// Copyright 2014-2017 Lars Knudsen, Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// +build ignore
package serial
package enumerator
import (
"log"

View File

@@ -1,10 +1,10 @@
//
// Copyright 2014-2016 Cristian Maglie. All rights reserved.
// Copyright 2014-2017 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
package serial // import "go.bug.st/serial.v1"
package enumerator // import "go.bug.st/serial.v1/enumerator"
import (
"fmt"
@@ -54,6 +54,8 @@ func parseDeviceID(deviceID string, details *PortDetails) {
// setupapi based
// --------------
//go:generate go run ../extras/mksyscall_windows.go -output syscall_windows.go usb_windows.go
//sys setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiClassGuidsFromNameW
//sys setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) = setupapi.SetupDiGetClassDevsW
//sys setupDiDestroyDeviceInfoList(set devicesSet) (err error) = setupapi.SetupDiDestroyDeviceInfoList
@@ -232,14 +234,14 @@ func (dev *deviceInfo) openDevRegKey(scope dicsScope, hwProfile uint32, keyType
func nativeGetDetailedPortsList() ([]*PortDetails, error) {
guids, err := classGuidsFromName("Ports")
if err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err}
return nil, &PortEnumerationError{causedBy: err}
}
var res []*PortDetails
for _, g := range guids {
devsSet, err := g.getDevicesSet()
if err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err}
return nil, &PortEnumerationError{causedBy: err}
}
defer devsSet.destroy()
@@ -260,7 +262,7 @@ func nativeGetDetailedPortsList() ([]*PortDetails, error) {
details.Name = portName
if err := retrievePortDetailsFromDevInfo(device, details); err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err}
return nil, &PortEnumerationError{causedBy: err}
}
res = append(res, details)
}

View File

@@ -1,10 +1,10 @@
//
// Copyright 2014-2016 Cristian Maglie. All rights reserved.
// Copyright 2014-2017 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
package serial // import "go.bug.st/serial.v1"
package enumerator // import "go.bug.st/serial.v1/enumerator"
import (
"testing"

View File

@@ -55,26 +55,6 @@ func GetPortsList() ([]string, error) {
return nativeGetPortsList()
}
// PortDetails contains detailed information about USB serial port.
// Use GetDetailedPortsList function to retrieve it.
type PortDetails struct {
Name string
IsUSB bool
VID string
PID string
SerialNumber string
// Manufacturer string
// Product string
}
// GetDetailedPortsList retrieve ports details like USB VID/PID.
// Please note that this function may not be available on all OS:
// in that case a FunctionNotImplemented error is returned.
func GetDetailedPortsList() ([]*PortDetails, error) {
return nativeGetDetailedPortsList()
}
// Mode describes a serial port configuration.
type Mode struct {
BaudRate int // The serial port bitrate (aka Baudrate)

View File

@@ -23,7 +23,7 @@ type windowsPort struct {
handle syscall.Handle
}
//go:generate go run extras/mksyscall_windows.go -output syscall_windows.go serial_windows.go usb_windows.go
//go:generate go run extras/mksyscall_windows.go -output syscall_windows.go serial_windows.go
//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW

View File

@@ -14,21 +14,13 @@ var _ unsafe.Pointer
var (
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modsetupapi = windows.NewLazySystemDLL("setupapi.dll")
procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW")
procGetCommState = modkernel32.NewProc("GetCommState")
procSetCommState = modkernel32.NewProc("SetCommState")
procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts")
procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction")
procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus")
procSetupDiClassGuidsFromNameW = modsetupapi.NewProc("SetupDiClassGuidsFromNameW")
procSetupDiGetClassDevsW = modsetupapi.NewProc("SetupDiGetClassDevsW")
procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList")
procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo")
procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW")
procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey")
procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW")
procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW")
procGetCommState = modkernel32.NewProc("GetCommState")
procSetCommState = modkernel32.NewProc("SetCommState")
procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts")
procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction")
procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus")
)
func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) {
@@ -86,92 +78,3 @@ func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) {
res = r0 != 0
return
}
func setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) {
var _p0 *uint16
_p0, err = syscall.UTF16PtrFromString(class)
if err != nil {
return
}
return _setupDiClassGuidsFromNameInternal(_p0, guid, guidSize, requiredSize)
}
func _setupDiClassGuidsFromNameInternal(class *uint16, guid *guid, guidSize uint32, requiredSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameW.Addr(), 4, uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(guid)), uintptr(guidSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) {
r0, _, e1 := syscall.Syscall6(procSetupDiGetClassDevsW.Addr(), 4, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(enumerator)), uintptr(hwndParent), uintptr(flags), 0, 0)
set = devicesSet(r0)
if set == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiDestroyDeviceInfoList(set devicesSet) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(set), 0, 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiEnumDeviceInfo(set devicesSet, index uint32, info *devInfoData) (err error) {
r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(set), uintptr(index), uintptr(unsafe.Pointer(info)))
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetDeviceInstanceId(set devicesSet, devInfo *devInfoData, devInstanceId unsafe.Pointer, devInstanceIdSize uint32, requiredSize *uint32) (err error) {
r1, _, e1 := syscall.Syscall6(procSetupDiGetDeviceInstanceIdW.Addr(), 5, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(devInstanceId), uintptr(devInstanceIdSize), uintptr(unsafe.Pointer(requiredSize)), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) {
r0, _, e1 := syscall.Syscall6(procSetupDiOpenDevRegKey.Addr(), 6, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(scope), uintptr(hwProfile), uintptr(keyType), uintptr(samDesired))
hkey = syscall.Handle(r0)
if hkey == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
func setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, outSize *uint32, reqSize *uint32) (res bool) {
r0, _, _ := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(property), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(outValue)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(reqSize)), 0, 0)
res = r0 != 0
return
}