go-daq / canbus

CAN-bus interface
BSD 3-Clause "New" or "Revised" License
31 stars 13 forks source link

Allow Configuration of CAN ID Mask #1

Closed collinbrake closed 1 year ago

collinbrake commented 1 year ago

This pull request changes the Send and Recv methods to require an additional idMask uint32 argument.

New syntax calling Recv to duplicate the original functionality:

sck, err = canbus.New()
id, data, err := sck.Recv(unix.CAN_SFF_MASK)

New syntax calling Recv to allow for extended (29-bit) CAN ID's:

sck, err = canbus.New()
id, data, err := sck.Recv(unix.CAN_EFF_MASK)
sbinet commented 1 year ago

thanks for the PR.

IUC, this kind of mask is usually applied at the socket level:

struct can_filter rfilter[2];

rfilter[0].can_id   = 0x123;
rfilter[0].can_mask = CAN_SFF_MASK;
rfilter[1].can_id   = 0x200;
rfilter[1].can_mask = 0x700;

setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));

https://www.kernel.org/doc/html/latest/networking/can.html

perhaps a better approach would be to replicate this kind of API (with a Go-idiomatic approach), with some kind of type Filter struct { ... } or type Options struct { ... } ?

WDYT ?

collinbrake commented 1 year ago

@sbinet My understanding is that filtering is a separate process from actually extracting the CAN ID from the frame. Please fill me in if that is incorrect. The kernel documentation you referenced states:

A filter matches, when:

& mask == can_id & mask

I take from that that the mask is only used to decide which frames are returned and which are discarded when recv is called. We still need to apply the mask to get the CAN ID from the frame of bits.

Perhaps there should be an additional field in the Socket type:

// Socket is a high-level representation of a CANBus socket.
type Socket struct {
    iface *net.Interface
    addr  *unix.SockaddrCAN
    dev   device
        rfilter []CanFilter
}

The CanFilter type is defined in the sys/unix package.

There would be an additional method of the Socket type as well:

// ApplyFilters sets the CAN_RAW_FILTER option of the socket
func (sck *Socket) ApplyFilters(rfilter []CanFilter) error {
     // sets rfilter and sets the socket filter option via unix method [SetsockoptCanRawFilter](https://cs.opensource.google/go/x/sys/+/2296e014:unix/syscall_linux.go;l=1344)
}

The rfilter field could be reused in Recv to extract the CAN ID from the frame bits.

sbinet commented 1 year ago

as you could probably tell from this package, my use of CANBUS was relatively naïve, so I am by no means an expert :)

I'd probably err on something like that:

type Socket struct {
   // ...
   filters []unix.CanFilter
}

func (sck *Socket) SetFilters(fs []unix.CanFilter) error {
    err := unix.SetsockoptCanRawFilter(sck.dev.fd, unix.SOL_CAN_RAW, unix.CAN_RAW_FILTER, fs)
    if err != nil {
        return fmt.Errorf("could not set CAN filters: %w", err)
    }
    sck.filters = fs
    return nil
}

// // Send sends data with a CAN_frame id to the CAN bus.
func (sck *Socket) Send(msg Message) (int, error) { ... }

func (sck *Socket) Recv() (msg Message, err error) { ... }

type Message struct {
    ID   uint32
    Data []byte
    Kind Kind
}

type Kind uint32
const (
    SFF Kind = iota
    EFF
    RTR
    ERR
)

with something like:

func ExampleSend() {
    sck, err := canbus.New()
    err = sck.SetFilters([]unix.CanFilter{ ... })

    _, err = sck.Send(canbus.Message{ID: 128, Data: []byte{0x1, 0xde, 0xad, 0xbe, 0xef}})
    _, err = sck.Send(canbus.Message{ID: 128, Data: []byte{0x1, 0xde, 0xad, 0xbe, 0xef}, Kind: canbus.SFF}) // same than above
    _, err = sck.Send(canbus.Message{ID: 128, Data: []byte{0x1, 0xde, 0xad, 0xbe, 0xef}, Kind: canbus.EFF})
}

what do you think ?