Skip to content

Commit bb2af19

Browse files
authored
Merge pull request #4 from ZaparooProject/fix-windows-detect
Improve windows port detection
2 parents fcc3f79 + c0d51d9 commit bb2af19

File tree

5 files changed

+133
-54
lines changed

5 files changed

+133
-54
lines changed

cmd/nfctest/testing.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func (t *Testing) TestCard(device *pn532.Device, detected *pn532.DetectedTag, mo
131131
return t.testFeliCaTag(tag, mode)
132132
case pn532.TagTypeUnknown:
133133
return errors.New("unknown tag type detected")
134-
case pn532.CardTypeAny:
134+
case pn532.TagTypeAny:
135135
return errors.New("generic card type - specific testing not available")
136136
default:
137137
return fmt.Errorf("unsupported tag type: %s", detected.Type)

detection.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ func shouldIncludeTag(tag *DetectedTag, tagType TagType, uid []byte) bool {
198198

199199
// matchesTagTypeFilter checks if tag matches the tag type filter
200200
func matchesTagTypeFilter(tag *DetectedTag, tagType TagType) bool {
201-
return tagType == CardTypeAny || tag.Type == tagType
201+
return tagType == TagTypeAny || tag.Type == tagType
202202
}
203203

204204
// matchesUIDFilter checks if tag UID matches the UID filter
@@ -360,7 +360,7 @@ func (d *Device) CreateTag(detected *DetectedTag) (Tag, error) {
360360
return NewFeliCaTag(d, detected.TargetData)
361361
case TagTypeUnknown:
362362
return nil, ErrInvalidTag
363-
case CardTypeAny:
363+
case TagTypeAny:
364364
return nil, ErrInvalidTag
365365
default:
366366
return nil, ErrInvalidTag

detection/uart/ports_windows.go

Lines changed: 127 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package uart
44

55
import (
6+
"errors"
67
"strings"
78
"unsafe"
89

@@ -12,14 +13,41 @@ import (
1213

1314
// getSerialPorts returns available serial ports on Windows
1415
func getSerialPorts() ([]serialPort, error) {
15-
// First, get COM ports from registry
16-
comPorts, err := getRegistryCOMPorts()
17-
if err != nil {
18-
return nil, err
16+
// First, try to get COM ports from registry
17+
registryPorts, registryErr := getRegistryCOMPorts()
18+
19+
// Get COM ports from SetupAPI (more comprehensive)
20+
setupAPIPorts, setupErr := getSetupAPICOMPorts()
21+
22+
// If both methods failed, return combined error information
23+
if registryErr != nil && setupErr != nil {
24+
return nil, errors.Join(registryErr, setupErr)
25+
}
26+
27+
// Merge ports from both sources, preferring SetupAPI data
28+
portMap := make(map[string]serialPort)
29+
30+
// Add registry ports first
31+
if registryErr == nil {
32+
for _, port := range registryPorts {
33+
portMap[port.Path] = port
34+
}
1935
}
2036

21-
// Then enrich with USB information using SetupAPI
22-
return enrichWithUSBInfo(comPorts)
37+
// Add/overwrite with SetupAPI ports (they have more metadata)
38+
if setupErr == nil {
39+
for _, port := range setupAPIPorts {
40+
portMap[port.Path] = port
41+
}
42+
}
43+
44+
// Convert map back to slice
45+
ports := make([]serialPort, 0, len(portMap))
46+
for _, port := range portMap {
47+
ports = append(ports, port)
48+
}
49+
50+
return ports, nil
2351
}
2452

2553
// getRegistryCOMPorts gets COM ports from Windows registry
@@ -52,8 +80,8 @@ func getRegistryCOMPorts() ([]serialPort, error) {
5280
return ports, nil
5381
}
5482

55-
// enrichWithUSBInfo adds USB device information to COM ports
56-
func enrichWithUSBInfo(ports []serialPort) ([]serialPort, error) {
83+
// getSetupAPICOMPorts gets COM ports directly from SetupAPI
84+
func getSetupAPICOMPorts() ([]serialPort, error) {
5785
// Load setupapi.dll
5886
setupapi := windows.NewLazySystemDLL("setupapi.dll")
5987
setupDiGetClassDevs := setupapi.NewProc("SetupDiGetClassDevsW")
@@ -78,16 +106,12 @@ func enrichWithUSBInfo(ports []serialPort) ([]serialPort, error) {
78106
DIGCF_PRESENT,
79107
)
80108

81-
if devInfo == 0 {
82-
return ports, nil // Return original ports without enrichment
109+
if devInfo == uintptr(windows.InvalidHandle) {
110+
return nil, windows.GetLastError()
83111
}
84112
defer setupDiDestroyDeviceInfoList.Call(devInfo)
85113

86-
// Create a map for quick lookup
87-
portMap := make(map[string]*serialPort)
88-
for i := range ports {
89-
portMap[ports[i].Path] = &ports[i]
90-
}
114+
var ports []serialPort
91115

92116
// Enumerate devices
93117
type spDevinfoData struct {
@@ -111,27 +135,43 @@ func enrichWithUSBInfo(ports []serialPort) ([]serialPort, error) {
111135
break
112136
}
113137

114-
// Get friendly name (includes COM port)
138+
// Get friendly name (includes COM port) using two-call pattern
115139
const SPDRP_FRIENDLYNAME = 0x0000000C
116-
var friendlyName [256]uint16
117140
var propertyType uint32
118-
var size uint32
141+
var requiredSize uint32
142+
143+
// First call to get the required buffer size
144+
setupDiGetDeviceRegistryProperty.Call(
145+
devInfo,
146+
uintptr(unsafe.Pointer(&devInfoData)),
147+
SPDRP_FRIENDLYNAME,
148+
0, // propertyType is optional on first call
149+
0, // nil buffer
150+
0, // buffer size 0
151+
uintptr(unsafe.Pointer(&requiredSize)),
152+
)
119153

154+
if requiredSize == 0 {
155+
continue
156+
}
157+
158+
// Allocate buffer of the required size and call again
159+
friendlyNameBuf := make([]uint16, requiredSize/2)
120160
ret, _, _ = setupDiGetDeviceRegistryProperty.Call(
121161
devInfo,
122162
uintptr(unsafe.Pointer(&devInfoData)),
123163
SPDRP_FRIENDLYNAME,
124164
uintptr(unsafe.Pointer(&propertyType)),
125-
uintptr(unsafe.Pointer(&friendlyName[0])),
126-
uintptr(uint32(len(friendlyName)*2)),
127-
uintptr(unsafe.Pointer(&size)),
165+
uintptr(unsafe.Pointer(&friendlyNameBuf[0])),
166+
uintptr(requiredSize),
167+
0, // requiredSize is optional on second call
128168
)
129169

130170
if ret == 0 {
131171
continue
132172
}
133173

134-
name := windows.UTF16ToString(friendlyName[:])
174+
name := windows.UTF16ToString(friendlyNameBuf)
135175

136176
// Extract COM port from friendly name
137177
var comPort string
@@ -145,56 +185,89 @@ func enrichWithUSBInfo(ports []serialPort) ([]serialPort, error) {
145185
continue
146186
}
147187

148-
// Find matching port
149-
port, exists := portMap[comPort]
150-
if !exists {
151-
continue
188+
// Create new port entry
189+
port := serialPort{
190+
Path: comPort,
191+
Name: name,
152192
}
153193

154-
// Get hardware ID for VID/PID
194+
// Get hardware ID for VID/PID using two-call pattern
155195
const SPDRP_HARDWAREID = 0x00000001
156-
var hardwareID [512]uint16
196+
var hwRequiredSize uint32
157197

158-
ret, _, _ = setupDiGetDeviceRegistryProperty.Call(
198+
// First call to get the required buffer size
199+
setupDiGetDeviceRegistryProperty.Call(
159200
devInfo,
160201
uintptr(unsafe.Pointer(&devInfoData)),
161202
SPDRP_HARDWAREID,
162-
uintptr(unsafe.Pointer(&propertyType)),
163-
uintptr(unsafe.Pointer(&hardwareID[0])),
164-
uintptr(uint32(len(hardwareID)*2)),
165-
uintptr(unsafe.Pointer(&size)),
203+
0, // propertyType is optional on first call
204+
0, // nil buffer
205+
0, // buffer size 0
206+
uintptr(unsafe.Pointer(&hwRequiredSize)),
166207
)
167208

168-
if ret != 0 {
169-
hwid := windows.UTF16ToString(hardwareID[:])
170-
// Parse VID/PID from hardware ID (format: USB\VID_xxxx&PID_xxxx)
171-
if vidpid := parseWindowsHardwareID(hwid); vidpid != "" {
172-
port.VIDPID = vidpid
209+
if hwRequiredSize > 0 {
210+
// Allocate buffer of the required size and call again
211+
hardwareIDBuf := make([]uint16, hwRequiredSize/2)
212+
ret, _, _ = setupDiGetDeviceRegistryProperty.Call(
213+
devInfo,
214+
uintptr(unsafe.Pointer(&devInfoData)),
215+
SPDRP_HARDWAREID,
216+
uintptr(unsafe.Pointer(&propertyType)),
217+
uintptr(unsafe.Pointer(&hardwareIDBuf[0])),
218+
uintptr(hwRequiredSize),
219+
0, // hwRequiredSize is optional on second call
220+
)
221+
222+
if ret != 0 {
223+
hwid := windows.UTF16ToString(hardwareIDBuf)
224+
// Parse VID/PID from hardware ID (format: USB\VID_xxxx&PID_xxxx)
225+
if vidpid := parseWindowsHardwareID(hwid); vidpid != "" {
226+
port.VIDPID = vidpid
227+
}
173228
}
174229
}
175230

176-
// Get manufacturer
231+
// Get manufacturer using two-call pattern
177232
const SPDRP_MFG = 0x0000000B
178-
var mfg [256]uint16
233+
var mfgRequiredSize uint32
179234

180-
ret, _, _ = setupDiGetDeviceRegistryProperty.Call(
235+
// First call to get the required buffer size
236+
setupDiGetDeviceRegistryProperty.Call(
181237
devInfo,
182238
uintptr(unsafe.Pointer(&devInfoData)),
183239
SPDRP_MFG,
184-
uintptr(unsafe.Pointer(&propertyType)),
185-
uintptr(unsafe.Pointer(&mfg[0])),
186-
uintptr(uint32(len(mfg)*2)),
187-
uintptr(unsafe.Pointer(&size)),
240+
0, // propertyType is optional on first call
241+
0, // nil buffer
242+
0, // buffer size 0
243+
uintptr(unsafe.Pointer(&mfgRequiredSize)),
188244
)
189245

190-
if ret != 0 {
191-
port.Manufacturer = windows.UTF16ToString(mfg[:])
246+
if mfgRequiredSize > 0 {
247+
// Allocate buffer of the required size and call again
248+
mfgBuf := make([]uint16, mfgRequiredSize/2)
249+
ret, _, _ = setupDiGetDeviceRegistryProperty.Call(
250+
devInfo,
251+
uintptr(unsafe.Pointer(&devInfoData)),
252+
SPDRP_MFG,
253+
uintptr(unsafe.Pointer(&propertyType)),
254+
uintptr(unsafe.Pointer(&mfgBuf[0])),
255+
uintptr(mfgRequiredSize),
256+
0, // mfgRequiredSize is optional on second call
257+
)
258+
259+
if ret != 0 {
260+
port.Manufacturer = windows.UTF16ToString(mfgBuf)
261+
}
192262
}
193263

194264
// Extract product from friendly name
195265
if n := strings.Index(name, " ("); n > 0 {
196266
port.Product = name[:n]
197267
}
268+
269+
// Add port to results
270+
ports = append(ports, port)
198271
}
199272

200273
return ports, nil
@@ -233,7 +306,13 @@ func parseWindowsHardwareID(hwid string) string {
233306
return vid + ":" + pid
234307
}
235308

236-
// getSerialPortsFallback not needed on Windows as registry always works
309+
// getSerialPortsFallback provides a fallback method for COM port detection
237310
func getSerialPortsFallback() ([]serialPort, error) {
311+
// Try SetupAPI first as it's more comprehensive
312+
if ports, err := getSetupAPICOMPorts(); err == nil {
313+
return ports, nil
314+
}
315+
316+
// Fallback to registry method
238317
return getRegistryCOMPorts()
239318
}

tag.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ const (
3939
TagTypeFeliCa TagType = "FELICA"
4040
// TagTypeUnknown represents unknown tag types.
4141
TagTypeUnknown TagType = "UNKNOWN"
42-
// CardTypeAny represents any tag type (for detection)
43-
CardTypeAny TagType = "ANY"
42+
// TagTypeAny represents any tag type (for detection)
43+
TagTypeAny TagType = "ANY"
4444
)
4545

4646
// Tag represents an NFC tag interface

tag_detection_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func TestTagTypeConstants(t *testing.T) {
9494
{"MIFARE", TagTypeMIFARE, "MIFARE"},
9595
{"FeliCa", TagTypeFeliCa, "FELICA"},
9696
{"Unknown", TagTypeUnknown, "UNKNOWN"},
97-
{"Any", CardTypeAny, "ANY"},
97+
{"Any", TagTypeAny, "ANY"},
9898
}
9999

100100
for _, tt := range tests {

0 commit comments

Comments
 (0)