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])) fmt.Printf("%v", string(buff[:n]))
} }
If a port is a virutal USB-CDC serial port (an USB-to-RS232 cable or a If a port is a virtual USB-CDC serial port (for example an USB-to-RS232
microcontroller development board are typical examples) is possible to cable or a microcontroller development board) is possible to retrieve
retrieve the USB metadata, like VID/PID or USB Serial Number, with the the USB metadata, like VID/PID or USB Serial Number, with the
GetDetailedPortsList function: GetDetailedPortsList function in the enumerator package:
ports, err := serial.GetDetailedPortsList() import "go.bug.st/serial.v1/enumerator"
ports, err := enumerator.GetDetailedPortsList()
if err != nil { if err != nil {
log.Fatal(err) 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" 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 // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// //
package serial_test package enumerator_test
import "fmt" import "fmt"
import "log" import "log"
import "go.bug.st/serial.v1" import "go.bug.st/serial.v1/enumerator"
func ExampleGetDetailedPortsList() { func ExampleGetDetailedPortsList() {
ports, err := serial.GetDetailedPortsList() ports, err := enumerator.GetDetailedPortsList()
if err != nil { if err != nil {
log.Fatal(err) 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 // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // 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 // #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -fconstant-cfstrings
// #include <IOKit/IOKitLib.h> // #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 // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // 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) { func nativeGetDetailedPortsList() ([]*PortDetails, error) {
// TODO // 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 // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // 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 ( import (
"bufio" "bufio"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"go.bug.st/serial.v1"
) )
func nativeGetDetailedPortsList() ([]*PortDetails, error) { func nativeGetDetailedPortsList() ([]*PortDetails, error) {
// Retrieve the port list // Retrieve the port list
ports, err := nativeGetPortsList() ports, err := serial.GetPortsList()
if err != nil { if err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} return nil, &PortEnumerationError{causedBy: err}
} }
var res []*PortDetails var res []*PortDetails
for _, port := range ports { for _, port := range ports {
details, err := nativeGetPortDetails(port) details, err := nativeGetPortDetails(port)
if err != nil { if err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} return nil, &PortEnumerationError{causedBy: err}
} }
res = append(res, details) 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 // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// //
// +build ignore // +build ignore
package serial package enumerator
import ( import (
"log" "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 // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // 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 ( import (
"fmt" "fmt"
@@ -54,6 +54,8 @@ func parseDeviceID(deviceID string, details *PortDetails) {
// setupapi based // 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 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 setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) = setupapi.SetupDiGetClassDevsW
//sys setupDiDestroyDeviceInfoList(set devicesSet) (err error) = setupapi.SetupDiDestroyDeviceInfoList //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) { func nativeGetDetailedPortsList() ([]*PortDetails, error) {
guids, err := classGuidsFromName("Ports") guids, err := classGuidsFromName("Ports")
if err != nil { if err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} return nil, &PortEnumerationError{causedBy: err}
} }
var res []*PortDetails var res []*PortDetails
for _, g := range guids { for _, g := range guids {
devsSet, err := g.getDevicesSet() devsSet, err := g.getDevicesSet()
if err != nil { if err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} return nil, &PortEnumerationError{causedBy: err}
} }
defer devsSet.destroy() defer devsSet.destroy()
@@ -260,7 +262,7 @@ func nativeGetDetailedPortsList() ([]*PortDetails, error) {
details.Name = portName details.Name = portName
if err := retrievePortDetailsFromDevInfo(device, details); err != nil { if err := retrievePortDetailsFromDevInfo(device, details); err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} return nil, &PortEnumerationError{causedBy: err}
} }
res = append(res, details) 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 // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // 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 ( import (
"testing" "testing"

View File

@@ -55,26 +55,6 @@ func GetPortsList() ([]string, error) {
return nativeGetPortsList() 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. // Mode describes a serial port configuration.
type Mode struct { type Mode struct {
BaudRate int // The serial port bitrate (aka Baudrate) BaudRate int // The serial port bitrate (aka Baudrate)

View File

@@ -23,7 +23,7 @@ type windowsPort struct {
handle syscall.Handle 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 //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 ( var (
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
modkernel32 = windows.NewLazySystemDLL("kernel32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modsetupapi = windows.NewLazySystemDLL("setupapi.dll")
procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW")
procGetCommState = modkernel32.NewProc("GetCommState") procGetCommState = modkernel32.NewProc("GetCommState")
procSetCommState = modkernel32.NewProc("SetCommState") procSetCommState = modkernel32.NewProc("SetCommState")
procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts")
procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction")
procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") 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")
) )
func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { 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 res = r0 != 0
return 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
}