Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 67 additions & 8 deletions pkg/readers/libnfc/libnfc.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,10 +375,7 @@ func (r *Reader) Detect(connected []string) string {

switch r.mode {
case modeACR122Only:
// Auto-detect for ACR122 and other USB/PCSC devices
if !helpers.Contains(connected, autoConnStr) {
return autoConnStr
}
return detectACR122Readers(connected)
case modeLegacyUART:
// Only detect UART devices, return with legacy prefix
device := detectSerialReaders(connected)
Expand All @@ -398,10 +395,7 @@ func (r *Reader) Detect(connected []string) string {
return device
}

// Auto-detect for ACR122 and other USB/PCSC devices
if !helpers.Contains(connected, autoConnStr) {
return autoConnStr
}
return detectACR122Readers(connected)
}

return ""
Expand Down Expand Up @@ -652,6 +646,69 @@ func detectI2CReaders(connected []string) string {
return ""
}

func detectACR122Readers(connected []string) string {
devices, err := nfc.ListDevices()
if err != nil {
log.Trace().Err(err).Msg("error listing libnfc devices")
return fallbackAutoACR122(connected)
}

return detectACR122Device(connected, devices)
}

func detectACR122Device(connected, devices []string) string {
foundACR122 := false
for _, device := range devices {
connStr := strings.TrimRight(device, "\x00")
if !isACR122ConnStr(connStr) {
continue
}
foundACR122 = true

path := connectionPath(connStr)
if path == "" || isConnectedPath(connected, path) {
continue
}

log.Trace().Msgf("acr122 libnfc reader found: %s", connStr)
return connStr
}

if foundACR122 {
return ""
}

return fallbackAutoACR122(connected)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

func fallbackAutoACR122(connected []string) string {
if helpers.Contains(connected, autoConnStr) || isConnectedPath(connected, "") {
return ""
}
return autoConnStr
}

func isACR122ConnStr(connStr string) bool {
return strings.HasPrefix(connStr, "acr122_usb:") || strings.HasPrefix(connStr, "acr122_pcsc:")
}

func connectionPath(connStr string) string {
parts := strings.SplitN(connStr, ":", 2)
if len(parts) != 2 {
return ""
}
return parts[1]
}

func isConnectedPath(connected []string, path string) bool {
for _, connStr := range connected {
if connectionPath(connStr) == path {
return true
}
}
return false
}

// toLibnfcConnStr translates internal normalized connection strings to the
// format expected by libnfc. libnfc driver names use underscores
// (e.g. "pn532_i2c", "pn532_uart") but ConnectionString() strips them for
Expand All @@ -665,6 +722,8 @@ func toLibnfcConnStr(mode readerMode, connStr string) string {
default:
connStr = strings.Replace(connStr, "pn532uart:", "pn532_uart:", 1)
connStr = strings.Replace(connStr, "pn532i2c:", "pn532_i2c:", 1)
connStr = strings.Replace(connStr, "acr122usb:", "acr122_usb:", 1)
connStr = strings.Replace(connStr, "acr122pcsc:", "acr122_pcsc:", 1)
return connStr
}
}
Expand Down
93 changes: 93 additions & 0 deletions pkg/readers/libnfc/libnfc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,67 @@ func TestOpenConnectionStringTranslation(t *testing.T) {
}
}

func TestDetectACR122Device(t *testing.T) {
tests := []struct {
name string
expected string
devices []string
connected []string
}{
{
name: "returns first concrete acr122 usb device",
devices: []string{"acr122_usb:003:004", "acr122_usb:003:005"},
expected: "acr122_usb:003:004",
},
{
name: "skips connected path from libnfcacr122 driver",
devices: []string{"acr122_usb:003:004", "acr122_usb:003:005"},
connected: []string{"libnfcacr122:003:004"},
expected: "acr122_usb:003:005",
},
{
name: "skips connected path from concrete driver",
devices: []string{"acr122_usb:003:004", "acr122_usb:003:005"},
connected: []string{"acr122_usb:003:004"},
expected: "acr122_usb:003:005",
},
{
name: "does not fall back when all concrete acr122 paths are connected",
devices: []string{"acr122_usb:003:004", "acr122_usb:003:005"},
connected: []string{"acr122_usb:003:004", "acr122_usb:003:005"},
expected: "",
},
{
name: "ignores non acr122 devices",
devices: []string{"pn532_uart:/dev/ttyUSB0", "acr122_pcsc:ACS ACR122U PICC Interface"},
expected: "acr122_pcsc:ACS ACR122U PICC Interface",
},
{
name: "trims nul from libnfc connection strings",
devices: []string{"acr122_usb:003:004\x00"},
expected: "acr122_usb:003:004",
},
{
name: "falls back to libnfc auto when no acr122 devices found",
devices: []string{"pn532_uart:/dev/ttyUSB0"},
expected: autoConnStr,
},
{
name: "does not return auto fallback when auto reader path is connected",
devices: []string{"pn532_uart:/dev/ttyUSB0"},
connected: []string{"libnfcacr122:"},
expected: "",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := detectACR122Device(tt.connected, tt.devices)
assert.Equal(t, tt.expected, result)
})
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// TestToLibnfcConnStr verifies that internal connection strings are translated
// to the libnfc format with underscored driver names (e.g. "pn532_i2c").
// Regression test: libnfc requires underscores in driver names but the internal
Expand Down Expand Up @@ -535,6 +596,20 @@ func TestToLibnfcConnStr(t *testing.T) {
expected: "pn532_uart:/dev/ttyUSB0",
},

// ACR122 device names
{
name: "acr122 usb normalized driver",
mode: modeACR122Only,
input: "acr122usb:003:004",
expected: "acr122_usb:003:004",
},
{
name: "acr122 pcsc normalized driver",
mode: modeACR122Only,
input: "acr122pcsc:ACS ACR122U PICC Interface",
expected: "acr122_pcsc:ACS ACR122U PICC Interface",
},

// Passthrough cases — strings that shouldn't be changed
{
name: "acr122 mode passthrough",
Expand Down Expand Up @@ -625,6 +700,24 @@ func TestToLibnfcConnStrEndToEnd(t *testing.T) {
},
expected: "pn532_uart:/dev/ttyUSB0",
},
{
name: "acr122_usb config to libnfc",
mode: modeACR122Only,
device: config.ReadersConnect{
Driver: "acr122_usb",
Path: "003:004",
},
expected: "acr122_usb:003:004",
},
{
name: "acr122_pcsc config to libnfc",
mode: modeACR122Only,
device: config.ReadersConnect{
Driver: "acr122_pcsc",
Path: "ACS ACR122U PICC Interface",
},
expected: "acr122_pcsc:ACS ACR122U PICC Interface",
},
}

for _, tt := range tests {
Expand Down
Loading