diff --git a/socket.go b/socket.go index 5e9f29f..ef35a56 100644 --- a/socket.go +++ b/socket.go @@ -7,6 +7,7 @@ package canbus import ( "encoding/binary" "errors" + "fmt" "io" "net" @@ -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) diff --git a/socket_example_test.go b/socket_example_test.go new file mode 100644 index 0000000..283b1c6 --- /dev/null +++ b/socket_example_test.go @@ -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) +} diff --git a/socket_test.go b/socket_test.go index 794ba96..d2e02a8 100644 --- a/socket_test.go +++ b/socket_test.go @@ -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) { @@ -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