Skip to content

Commit

Permalink
canbus: add SetFilters
Browse files Browse the repository at this point in the history
This CL adds the SetFilters API around the setsockopt(3) UNIX call.

Signed-off-by: Sebastien Binet <[email protected]>
  • Loading branch information
sbinet committed Sep 5, 2022
1 parent e36537d commit 9f5e161
Show file tree
Hide file tree
Showing 3 changed files with 367 additions and 0 deletions.
12 changes: 12 additions & 0 deletions socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package canbus
import (
"encoding/binary"
"errors"
"fmt"
"io"
"net"

Expand Down Expand Up @@ -42,6 +43,17 @@ func (sck *Socket) Name() string {
return sck.iface.Name
}

// SetFilters sets the CAN_RAW_FILTER option and applies the provided
// filters to the underlying socket.
func (sck *Socket) SetFilters(filters []unix.CanFilter) error {
err := unix.SetsockoptCanRawFilter(sck.dev.fd, unix.SOL_CAN_RAW, unix.CAN_RAW_FILTER, filters)
if err != nil {
return fmt.Errorf("could not set CAN filters: %w", err)
}

return nil
}

// Close closes the CAN bus socket.
func (sck *Socket) Close() error {
return unix.Close(sck.dev.fd)
Expand Down
207 changes: 207 additions & 0 deletions socket_example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// Copyright 2022 The go-daq Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package canbus_test

import (
"fmt"
"log"

"github.com/go-daq/canbus"
"golang.org/x/sys/unix"
)

func ExampleSocket() {
recv, err := canbus.New()
if err != nil {
log.Fatal(err)
}
defer recv.Close()

send, err := canbus.New()
if err != nil {
log.Fatal(err)
}
defer send.Close()

err = recv.Bind("vcan0")
if err != nil {
log.Fatalf("could not bind recv socket: %+v", err)
}

err = send.Bind("vcan0")
if err != nil {
log.Fatalf("could not bind send socket: %+v", err)
}

for i := 0; i < 5; i++ {
_, err := send.Send(canbus.Frame{
ID: 0x123,
Data: []byte(fmt.Sprintf("data-%02d", i)),
Kind: canbus.SFF,
})
if err != nil {
log.Fatalf("could not send frame %d: %+v", i, err)
}
}

for i := 0; i < 5; i++ {
frame, err := recv.Recv()
if err != nil {
log.Fatalf("could not recv frame %d: %+v", i, err)
}
fmt.Printf("frame-%02d: %q (id=0x%x)\n", i, frame.Data, frame.ID)
}

// Output:
// frame-00: "data-00" (id=0x123)
// frame-01: "data-01" (id=0x123)
// frame-02: "data-02" (id=0x123)
// frame-03: "data-03" (id=0x123)
// frame-04: "data-04" (id=0x123)
}

func ExampleSocket_eFF() {
recv, err := canbus.New()
if err != nil {
log.Fatal(err)
}
defer recv.Close()

send, err := canbus.New()
if err != nil {
log.Fatal(err)
}
defer send.Close()

err = recv.Bind("vcan0")
if err != nil {
log.Fatalf("could not bind recv socket: %+v", err)
}

err = send.Bind("vcan0")
if err != nil {
log.Fatalf("could not bind send socket: %+v", err)
}

for i := 0; i < 5; i++ {
_, err := send.Send(canbus.Frame{
ID: 0x1234567,
Data: []byte(fmt.Sprintf("data-%02d", i)),
Kind: canbus.EFF,
})
if err != nil {
log.Fatalf("could not send frame %d: %+v", i, err)
}
}

for i := 0; i < 5; i++ {
frame, err := recv.Recv()
if err != nil {
log.Fatalf("could not recv frame %d: %+v", i, err)
}
fmt.Printf("frame-%02d: %q (id=0x%x)\n", i, frame.Data, frame.ID)
}

// Output:
// frame-00: "data-00" (id=0x1234567)
// frame-01: "data-01" (id=0x1234567)
// frame-02: "data-02" (id=0x1234567)
// frame-03: "data-03" (id=0x1234567)
// frame-04: "data-04" (id=0x1234567)
}

func ExampleSocket_setFilters() {
recv1, err := canbus.New()
if err != nil {
log.Fatal(err)
}
defer recv1.Close()

err = recv1.SetFilters([]unix.CanFilter{
{Id: 0x123, Mask: unix.CAN_SFF_MASK},
})
if err != nil {
log.Fatalf("could not set CAN filters: %+v", err)
}

recv2, err := canbus.New()
if err != nil {
log.Fatal(err)
}
defer recv2.Close()

err = recv2.SetFilters([]unix.CanFilter{
{Id: 0x123 | unix.CAN_INV_FILTER, Mask: unix.CAN_SFF_MASK},
})
if err != nil {
log.Fatalf("could not set CAN filters: %+v", err)
}

send, err := canbus.New()
if err != nil {
log.Fatal(err)
}
defer send.Close()

err = recv1.Bind("vcan0")
if err != nil {
log.Fatalf("could not bind recv socket: %+v", err)
}

err = recv2.Bind("vcan0")
if err != nil {
log.Fatalf("could not bind recv socket: %+v", err)
}

err = send.Bind("vcan0")
if err != nil {
log.Fatalf("could not bind send socket: %+v", err)
}

for i := 0; i < 6; i++ {
var id uint32 = 0x123
if i%2 == 0 {
id = 0x321
}
_, err := send.Send(canbus.Frame{
ID: id,
Data: []byte(fmt.Sprintf("data-%02d", i)),
Kind: canbus.SFF,
})
if err != nil {
log.Fatalf("could not send frame %d: %+v", i, err)
}
}

fmt.Printf("recv1:\n")
for i := 0; i < 3; i++ {
frame, err := recv1.Recv()
if err != nil {
log.Fatalf("could not recv frame %d: %+v", i, err)
}
fmt.Printf("frame-%02d: %q (id=0x%x)\n", i, frame.Data, frame.ID)
}

fmt.Printf("\n")
fmt.Printf("recv2:\n")
for i := 0; i < 3; i++ {
frame, err := recv2.Recv()
if err != nil {
log.Fatalf("could not recv frame %d: %+v", i, err)
}
fmt.Printf("frame-%02d: %q (id=0x%x)\n", i, frame.Data, frame.ID)
}

// Output:
// recv1:
// frame-00: "data-01" (id=0x123)
// frame-01: "data-03" (id=0x123)
// frame-02: "data-05" (id=0x123)
//
// recv2:
// frame-00: "data-00" (id=0x321)
// frame-01: "data-02" (id=0x321)
// frame-02: "data-04" (id=0x321)
}
148 changes: 148 additions & 0 deletions socket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (
"fmt"
"log"
"reflect"
"strings"
"testing"

"github.com/go-daq/canbus"
"golang.org/x/sys/unix"
)

func TestSocket(t *testing.T) {
Expand Down Expand Up @@ -150,6 +152,152 @@ func TestErrLength(t *testing.T) {
}
}

func TestSetFilters(t *testing.T) {
const (
endpoint = "vcan0"
kind = canbus.EFF
)

r1, err := canbus.New()
if err != nil {
t.Fatal(err)
}
defer r1.Close()

err = r1.SetFilters([]unix.CanFilter{
{Id: 0x123, Mask: unix.CAN_SFF_MASK},
})
if err != nil {
t.Fatalf("could not set CAN filters: %+v", err)
}

r2, err := canbus.New()
if err != nil {
t.Fatal(err)
}
defer r2.Close()

err = r2.SetFilters([]unix.CanFilter{
{Id: 0xddd, Mask: unix.CAN_SFF_MASK},
})
if err != nil {
t.Fatalf("could not set CAN filters: %+v", err)
}

r3, err := canbus.New()
if err != nil {
t.Fatal(err)
}
defer r3.Close()

err = r3.SetFilters([]unix.CanFilter{
{Id: 0xddd | unix.CAN_INV_FILTER, Mask: unix.CAN_SFF_MASK},
})
if err != nil {
t.Fatalf("could not set CAN filters: %+v", err)
}

r4, err := canbus.New()
if err != nil {
t.Fatal(err)
}
defer r2.Close()

w, err := canbus.New()
if err != nil {
t.Fatal(err)
}
defer w.Close()

err = r1.Bind(endpoint)
if err != nil {
log.Fatal(err)
}

err = r2.Bind(endpoint)
if err != nil {
log.Fatal(err)
}

err = r3.Bind(endpoint)
if err != nil {
log.Fatal(err)
}

err = r4.Bind(endpoint)
if err != nil {
log.Fatal(err)
}

err = w.Bind(endpoint)
if err != nil {
log.Fatal(err)
}

for i := 0; i < 4; i++ {
var id uint32 = 0x123
if i%2 == 0 {
id = 0xddd
}
_, err = w.Send(canbus.Frame{
ID: id,
Data: []byte(fmt.Sprintf("%02d", i)),
Kind: kind,
})
if err != nil {
t.Fatalf("could not send frame %d: %+v", i, err)
}
}

recv := func(r *canbus.Socket, n int) ([]string, error) {
var msgs []string
for i := 0; i < n; i++ {
frame, err := r.Recv()
if err != nil {
return nil, fmt.Errorf("could retrieve frame %d: %+v", i, err)
}
msgs = append(msgs, string(frame.Data))
}
return msgs, nil
}

got, err := recv(r1, 2)
if err != nil {
t.Fatalf("filtered-socket: %+v", err)
}

if got, want := strings.Join(got, ","), "01,03"; got != want {
t.Fatalf("r1 filter failed:\ngot= %q\nwant=%q\n", got, want)
}

got, err = recv(r2, 2)
if err != nil {
t.Fatalf("filtered-socket: %+v", err)
}

if got, want := strings.Join(got, ","), "00,02"; got != want {
t.Fatalf("r2 filter failed:\ngot= %q\nwant=%q\n", got, want)
}

got, err = recv(r3, 2)
if err != nil {
t.Fatalf("filtered-socket: %+v", err)
}

if got, want := strings.Join(got, ","), "01,03"; got != want {
t.Fatalf("r3 filter failed:\ngot= %q\nwant=%q\n", got, want)
}

got, err = recv(r4, 4)
if err != nil {
t.Fatalf("non-filtered socket: %+v", err)
}

if got, want := strings.Join(got, ","), "00,01,02,03"; got != want {
t.Fatalf("r4 nofilter failed:\ngot= %q\nwant=%q\n", got, want)
}
}

func BenchmarkSend(b *testing.B) {
for _, bc := range []struct {
kind canbus.Kind
Expand Down

0 comments on commit 9f5e161

Please sign in to comment.