Skip to content

Commit 5ced18b

Browse files
committed
use native serialport library to handle vid/pid
1 parent 66ed59a commit 5ced18b

File tree

4 files changed

+18
-311
lines changed

4 files changed

+18
-311
lines changed

seriallist.go

+13-5
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
package main
44

55
import (
6+
"fmt"
67
log "github.com/Sirupsen/logrus"
7-
"github.com/facchinm/go-serial"
8+
"github.com/facchinm/go-serial-native"
89
"regexp"
910
)
1011

@@ -16,6 +17,7 @@ type OsSerialPort struct {
1617
Product string
1718
IdProduct string
1819
IdVendor string
20+
ISerial string
1921
NetworkPort bool
2022
}
2123

@@ -29,11 +31,17 @@ func GetList(network bool) ([]OsSerialPort, error) {
2931
} else {
3032

3133
// will timeout in 2 seconds
32-
ports, err := serial.GetPortsList()
33-
3434
arrPorts := []OsSerialPort{}
35+
ports, err := serial.ListPorts()
36+
if err != nil {
37+
return arrPorts, err
38+
}
39+
3540
for _, element := range ports {
36-
arrPorts = append(arrPorts, OsSerialPort{Name: element})
41+
vid, pid, _ := element.USBVIDPID()
42+
vidString := fmt.Sprintf("0x%04X", vid)
43+
pidString := fmt.Sprintf("0x%04X", pid)
44+
arrPorts = append(arrPorts, OsSerialPort{Name: element.Name(), IdVendor: vidString, IdProduct: pidString, ISerial: element.USBSerialNumber()})
3745
}
3846

3947
// see if we should filter the list
@@ -54,7 +62,7 @@ func GetList(network bool) ([]OsSerialPort, error) {
5462
arrPorts = newarrPorts
5563
}
5664

57-
arrPorts = associateVidPidWithPort(arrPorts)
65+
arrPorts = ExtraFilterPorts(arrPorts)
5866
return arrPorts, err
5967
//log.Printf("Done doing GetList(). arrPorts:%v\n", arrPorts)
6068
}

seriallist_darwin.go

+1-53
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,14 @@
11
package main
22

33
import (
4-
"os/exec"
54
"strings"
6-
7-
"github.com/arduino/arduino-create-agent/utilities"
85
)
96

10-
// execute system_profiler SPUSBDataType | grep "Vendor ID: 0x2341" -A5 -B2
11-
// maybe -B2 is not necessary
12-
// trim whitespace and eol
13-
// map everything with :
14-
// get [map][Location ID] first 5 chars
15-
// search all board.txt files for map[Product ID]
16-
// assign it to name
17-
18-
func associateVidPidWithPort(ports []OsSerialPort) []OsSerialPort {
7+
func ExtraFilterPorts(ports []OsSerialPort) []OsSerialPort {
198

209
// prefilter ports
2110
ports = Filter(ports, func(port OsSerialPort) bool {
2211
return !strings.Contains(port.Name, "Blue") && !strings.Contains(port.Name, "/cu")
2312
})
24-
25-
for index, _ := range ports {
26-
port_hash := strings.Trim(ports[index].Name, "/dev/tty.usbmodem")
27-
port_hash = strings.Trim(port_hash, "/dev/tty.usbserial-")
28-
29-
port_hash = strings.ToLower(port_hash)
30-
31-
usbcmd := exec.Command("system_profiler", "SPUSBDataType")
32-
grepcmd := exec.Command("grep", "Location ID: 0x"+port_hash[:len(port_hash)-1], "-B6")
33-
cmdOutput, _ := utilities.PipeCommands(usbcmd, grepcmd)
34-
35-
if len(cmdOutput) == 0 {
36-
usbcmd = exec.Command("system_profiler", "SPUSBDataType")
37-
grepcmd = exec.Command("grep" /*"Serial Number: "+*/, strings.Trim(port_hash, "0"), "-B3", "-A3")
38-
cmdOutput, _ = utilities.PipeCommands(usbcmd, grepcmd)
39-
}
40-
41-
if len(cmdOutput) == 0 {
42-
//give up
43-
continue
44-
}
45-
46-
cmdOutSlice := strings.Split(string(cmdOutput), "\n")
47-
48-
cmdOutMap := make(map[string]string)
49-
50-
for _, element := range cmdOutSlice {
51-
if strings.Contains(element, "ID") || strings.Contains(element, "Manufacturer") {
52-
element = strings.TrimSpace(element)
53-
arr := strings.Split(element, ": ")
54-
if len(arr) > 1 {
55-
cmdOutMap[arr[0]] = arr[1]
56-
} else {
57-
cmdOutMap[arr[0]] = ""
58-
}
59-
}
60-
}
61-
ports[index].IdProduct = strings.Split(cmdOutMap["Product ID"], " ")[0]
62-
ports[index].IdVendor = strings.Split(cmdOutMap["Vendor ID"], " ")[0]
63-
ports[index].Manufacturer = cmdOutMap["Manufacturer"]
64-
}
6513
return ports
6614
}

seriallist_linux.go

100755100644
+2-31
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,7 @@
11
package main
22

3-
import (
4-
"fmt"
5-
"os/exec"
6-
"path/filepath"
7-
"strconv"
8-
"strings"
3+
import ()
94

10-
"github.com/arduino/arduino-create-agent/utilities"
11-
)
12-
13-
func associateVidPidWithPort(ports []OsSerialPort) []OsSerialPort {
14-
15-
for index, _ := range ports {
16-
ueventcmd := exec.Command("cat", "/sys/class/tty/"+filepath.Base(ports[index].Name)+"/device/uevent")
17-
grep3cmd := exec.Command("grep", "PRODUCT=")
18-
19-
cmdOutput2, _ := utilities.PipeCommands(ueventcmd, grep3cmd)
20-
cmdOutput2S := string(cmdOutput2)
21-
22-
if len(cmdOutput2S) == 0 {
23-
continue
24-
}
25-
26-
infos := strings.Split(cmdOutput2S, "=")
27-
28-
vid_pid := strings.Split(infos[1], "/")
29-
30-
vid, _ := strconv.ParseInt(vid_pid[0], 16, 32)
31-
pid, _ := strconv.ParseInt(vid_pid[1], 16, 32)
32-
ports[index].IdVendor = fmt.Sprintf("0x%04x", vid)
33-
ports[index].IdProduct = fmt.Sprintf("0x%04x", pid)
34-
}
5+
func ExtraFilterPorts(ports []OsSerialPort) []OsSerialPort {
356
return ports
367
}

seriallist_windows.go

+2-222
Original file line numberDiff line numberDiff line change
@@ -1,227 +1,7 @@
11
package main
22

3-
import (
4-
"os"
5-
"regexp"
6-
"strings"
7-
"sync"
3+
import ()
84

9-
log "github.com/Sirupsen/logrus"
10-
"github.com/mattn/go-ole"
11-
"github.com/mattn/go-ole/oleutil"
12-
)
13-
14-
var (
15-
serialListWindowsWg sync.WaitGroup
16-
)
17-
18-
func associateVidPidWithPort(ports []OsSerialPort) []OsSerialPort {
19-
ports, _ = getList()
5+
func ExtraFilterPorts(ports []OsSerialPort) []OsSerialPort {
206
return ports
217
}
22-
23-
func getList() ([]OsSerialPort, os.SyscallError) {
24-
// use a queue to do this to avoid conflicts
25-
// we've been getting crashes when this getList is requested
26-
// too many times too fast. i think it's something to do with
27-
// the unsafe syscalls overwriting memory
28-
29-
// this will only block if waitgroupctr > 0. so first time
30-
// in shouldn't block
31-
serialListWindowsWg.Wait()
32-
33-
serialListWindowsWg.Add(1)
34-
arr, sysCallErr := getListViaWmiPnpEntity()
35-
serialListWindowsWg.Done()
36-
//arr = make([]OsSerialPort, 0)
37-
38-
// see if array has any data, if not fallback to the traditional
39-
// com port list model
40-
/*
41-
if len(arr) == 0 {
42-
// assume it failed
43-
arr, sysCallErr = getListViaOpen()
44-
}
45-
*/
46-
47-
// see if array has any data, if not fallback to looking at
48-
// the registry list
49-
/*
50-
arr = make([]OsSerialPort, 0)
51-
if len(arr) == 0 {
52-
// assume it failed
53-
arr, sysCallErr = getListViaRegistry()
54-
}
55-
*/
56-
57-
return arr, sysCallErr
58-
}
59-
60-
func getListSynchronously() {
61-
62-
}
63-
64-
func getListViaWmiPnpEntity() ([]OsSerialPort, os.SyscallError) {
65-
66-
//log.Println("Doing getListViaWmiPnpEntity()")
67-
68-
// this method panics a lot and i'm not sure why, just catch
69-
// the panic and return empty list
70-
defer func() {
71-
if e := recover(); e != nil {
72-
// e is the interface{} typed-value we passed to panic()
73-
log.Println("Got panic: ", e) // Prints "Whoops: boom!"
74-
}
75-
}()
76-
77-
var err os.SyscallError
78-
79-
//var friendlyName string
80-
81-
// init COM, oh yeah
82-
ole.CoInitialize(0)
83-
defer ole.CoUninitialize()
84-
85-
unknown, _ := oleutil.CreateObject("WbemScripting.SWbemLocator")
86-
defer unknown.Release()
87-
88-
wmi, _ := unknown.QueryInterface(ole.IID_IDispatch)
89-
defer wmi.Release()
90-
91-
// service is a SWbemServices
92-
serviceRaw, _ := oleutil.CallMethod(wmi, "ConnectServer")
93-
service := serviceRaw.ToIDispatch()
94-
defer service.Release()
95-
96-
// result is a SWBemObjectSet
97-
//pname := syscall.StringToUTF16("SELECT * FROM Win32_PnPEntity where Name like '%" + "COM35" + "%'")
98-
pname := "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0 and Name like '%(COM%'"
99-
//pname := "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0"
100-
resultRaw, err2 := oleutil.CallMethod(service, "ExecQuery", pname)
101-
//log.Println("Got result from oleutil.CallMethod")
102-
if err2 != nil {
103-
// we got back an error or empty list
104-
log.Printf("Got an error back from oleutil.CallMethod. err:%v", err2)
105-
return nil, err
106-
}
107-
108-
result := resultRaw.ToIDispatch()
109-
defer result.Release()
110-
111-
countVar, _ := oleutil.GetProperty(result, "Count")
112-
count := int(countVar.Val)
113-
114-
list := make([]OsSerialPort, count)
115-
116-
for i := 0; i < count; i++ {
117-
118-
// items we're looping thru look like below and
119-
// thus we can query for any of these names
120-
/*
121-
__GENUS : 2
122-
__CLASS : Win32_PnPEntity
123-
__SUPERCLASS : CIM_LogicalDevice
124-
__DYNASTY : CIM_ManagedSystemElement
125-
__RELPATH : Win32_PnPEntity.DeviceID="USB\\VID_1D50&PID_606D&MI_02\\6&2F09EA14&0&0002"
126-
__PROPERTY_COUNT : 24
127-
__DERIVATION : {CIM_LogicalDevice, CIM_LogicalElement, CIM_ManagedSystemElement}
128-
__SERVER : JOHN-ATIV
129-
__NAMESPACE : root\cimv2
130-
__PATH : \\JOHN-ATIV\root\cimv2:Win32_PnPEntity.DeviceID="USB\\VID_1D50&PID_606D&MI_02\\6&2F09EA14
131-
&0&0002"
132-
Availability :
133-
Caption : TinyG v2 (Data Channel) (COM12)
134-
ClassGuid : {4d36e978-e325-11ce-bfc1-08002be10318}
135-
CompatibleID : {USB\Class_02&SubClass_02&Prot_01, USB\Class_02&SubClass_02, USB\Class_02}
136-
ConfigManagerErrorCode : 0
137-
ConfigManagerUserConfig : False
138-
CreationClassName : Win32_PnPEntity
139-
Description : TinyG v2 (Data Channel)
140-
DeviceID : USB\VID_1D50&PID_606D&MI_02\6&2F09EA14&0&0002
141-
ErrorCleared :
142-
ErrorDescription :
143-
HardwareID : {USB\VID_1D50&PID_606D&REV_0097&MI_02, USB\VID_1D50&PID_606D&MI_02}
144-
InstallDate :
145-
LastErrorCode :
146-
Manufacturer : Synthetos (www.synthetos.com)
147-
Name : TinyG v2 (Data Channel) (COM12)
148-
PNPDeviceID : USB\VID_1D50&PID_606D&MI_02\6&2F09EA14&0&0002
149-
PowerManagementCapabilities :
150-
PowerManagementSupported :
151-
Service : usbser
152-
Status : OK
153-
StatusInfo :
154-
SystemCreationClassName : Win32_ComputerSystem
155-
SystemName : JOHN-ATIV
156-
PSComputerName : JOHN-ATIV
157-
*/
158-
159-
// item is a SWbemObject, but really a Win32_Process
160-
itemRaw, _ := oleutil.CallMethod(result, "ItemIndex", i)
161-
item := itemRaw.ToIDispatch()
162-
defer item.Release()
163-
164-
asString, _ := oleutil.GetProperty(item, "Name")
165-
166-
//log.Println(asString.ToString())
167-
168-
// get the com port
169-
//if false {
170-
s := strings.Split(asString.ToString(), "(COM")[1]
171-
s = "COM" + s
172-
s = strings.Split(s, ")")[0]
173-
list[i].Name = s
174-
//}
175-
176-
// get the deviceid so we can figure out related ports
177-
// it will look similar to
178-
// USB\VID_1D50&PID_606D&MI_00\6&2F09EA14&0&0000
179-
deviceIdStr, _ := oleutil.GetProperty(item, "DeviceID")
180-
devIdItems := strings.Split(deviceIdStr.ToString(), "&")
181-
//log.Printf("DeviceId elements:%v", devIdItems)
182-
if len(devIdItems) > 3 {
183-
list[i].SerialNumber = devIdItems[3]
184-
list[i].IdProduct = strings.Replace(devIdItems[1], "PID_", "", 1)
185-
list[i].IdVendor = strings.Replace(devIdItems[0], "USB\\VID_", "", 1)
186-
} else {
187-
list[i].SerialNumber = deviceIdStr.ToString()
188-
pidMatch := regexp.MustCompile("PID_(\\d+)").FindAllStringSubmatch(deviceIdStr.ToString(), -1)
189-
if len(pidMatch) > 0 {
190-
if len(pidMatch[0]) > 1 {
191-
list[i].IdProduct = pidMatch[0][1]
192-
}
193-
}
194-
vidMatch := regexp.MustCompile("VID_(\\d+)").FindAllStringSubmatch(deviceIdStr.ToString(), -1)
195-
if len(vidMatch) > 0 {
196-
if len(vidMatch[0]) > 1 {
197-
list[i].IdVendor = vidMatch[0][1]
198-
}
199-
}
200-
}
201-
202-
list[i].IdVendor = "0x" + list[i].IdVendor
203-
list[i].IdProduct = "0x" + list[i].IdProduct
204-
205-
manufStr, _ := oleutil.GetProperty(item, "Manufacturer")
206-
list[i].Manufacturer = manufStr.ToString()
207-
descStr, _ := oleutil.GetProperty(item, "Description")
208-
list[i].Product = descStr.ToString()
209-
//classStr, _ := oleutil.GetProperty(item, "CreationClassName")
210-
//list[i].DeviceClass = classStr.ToString()
211-
212-
}
213-
214-
return list, err
215-
}
216-
217-
func convertByteArrayToUint16Array(b []byte, mylen uint32) []uint16 {
218-
219-
log.Println("converting. len:", mylen)
220-
var i uint32
221-
ret := make([]uint16, mylen/2)
222-
for i = 0; i < mylen; i += 2 {
223-
//ret[i/2] = binary.LittleEndian.Uint16(b[i : i+1])
224-
ret[i/2] = uint16(b[i]) | uint16(b[i+1])<<8
225-
}
226-
return ret
227-
}

0 commit comments

Comments
 (0)