gotelem/socketcan/socketcan.go

207 lines
4.3 KiB
Go
Raw Permalink Normal View History

2023-05-09 05:54:20 +00:00
//go:build linux
2023-05-03 15:38:37 +00:00
/*
2023-05-05 01:49:04 +00:00
Package socketcan provides a wrapper around the Linux socketCAN interface.
2023-05-03 15:38:37 +00:00
*/
2023-04-15 05:01:36 +00:00
package socketcan
2023-04-14 20:42:16 +00:00
import (
"encoding/binary"
"errors"
"fmt"
"net"
"github.com/kschamplin/gotelem"
2023-04-14 20:42:16 +00:00
"golang.org/x/sys/unix"
)
// A CanSocket is a CAN device that uses the socketCAN linux drivers to write to real
// CAN hardware.
2023-04-14 20:42:16 +00:00
type CanSocket struct {
iface *net.Interface
addr *unix.SockaddrCAN
fd int
}
2023-05-09 05:54:20 +00:00
type CanFilter interface {
Inverted() bool
Mask() uint32
Id() uint32
}
2023-05-03 15:38:37 +00:00
// standardFrameSize is the full size in bytes of the default CAN frame.
2023-04-14 20:42:16 +00:00
const standardFrameSize = unix.CAN_MTU
// we use the base CAN_MTU since the FD MTU is not in sys/unix. but we know it's +64-8 bytes
const fdFrameSize = unix.CAN_MTU + 56
2023-04-14 20:42:16 +00:00
2023-05-03 15:38:37 +00:00
// NewCanSocket constructs a new CanSocket and binds it to the interface given by ifname
2023-04-14 20:42:16 +00:00
func NewCanSocket(ifname string) (*CanSocket, error) {
var sck CanSocket
fd, err := unix.Socket(unix.AF_CAN, unix.SOCK_RAW, unix.CAN_RAW)
if err != nil {
return nil, err
}
sck.fd = fd
iface, err := net.InterfaceByName(ifname)
if err != nil {
return nil, err
}
sck.iface = iface
sck.addr = &unix.SockaddrCAN{Ifindex: sck.iface.Index}
err = unix.Bind(sck.fd, sck.addr)
if err != nil {
return nil, err
}
return &sck, nil
}
2023-05-03 15:38:37 +00:00
// Close closes the socket.
2023-04-14 20:42:16 +00:00
func (sck *CanSocket) Close() error {
return unix.Close(sck.fd)
}
2023-05-03 15:38:37 +00:00
// Name returns the name of the socket.
2023-04-14 20:42:16 +00:00
func (sck *CanSocket) Name() string {
return sck.iface.Name
}
2023-05-03 15:38:37 +00:00
// SetErrFilter sets if error packets should be sent upstream
2023-04-14 20:42:16 +00:00
func (sck *CanSocket) SetErrFilter(shouldFilter bool) error {
var err error
var errmask = 0
if shouldFilter {
errmask = unix.CAN_ERR_MASK
}
err = unix.SetsockoptInt(sck.fd, unix.SOL_CAN_RAW, unix.CAN_RAW_ERR_FILTER, errmask)
return err
}
// SetFDMode enables or disables the transmission of CAN FD packets.
func (sck *CanSocket) SetFDMode(enable bool) error {
var val int
if enable {
val = 1
} else {
val = 0
2023-04-14 20:42:16 +00:00
}
err := unix.SetsockoptInt(sck.fd, unix.SOL_CAN_RAW, unix.CAN_RAW_FD_FRAMES, val)
return err
2023-04-14 20:42:16 +00:00
}
// SetFilters will set the socketCAN filters based on a standard CAN filter list.
2023-05-09 05:54:20 +00:00
func (sck *CanSocket) SetFilters(filters []CanFilter) error {
// helper function to make a filter.
// id and mask are straightforward, if inverted is true, the filter
// will reject anything that matches.
2023-05-09 05:54:20 +00:00
makeFilter := func(filter CanFilter) unix.CanFilter {
f := unix.CanFilter{Id: filter.Id(), Mask: filter.Mask()}
2023-05-09 05:54:20 +00:00
if filter.Inverted() {
f.Id = f.Id | unix.CAN_INV_FILTER
}
return f
}
convertedFilters := make([]unix.CanFilter, len(filters))
for i, filt := range filters {
convertedFilters[i] = makeFilter(filt)
}
return unix.SetsockoptCanRawFilter(sck.fd, unix.SOL_CAN_RAW, unix.CAN_RAW_FILTER, convertedFilters)
2023-04-14 20:42:16 +00:00
}
2023-05-03 15:38:37 +00:00
// Send sends a CAN frame
func (sck *CanSocket) Send(msg *gotelem.Frame) error {
2023-04-14 20:42:16 +00:00
buf := make([]byte, fdFrameSize)
idToWrite := msg.Id
switch msg.Kind {
2023-05-09 15:25:29 +00:00
case gotelem.CanSFFFrame:
idToWrite &= unix.CAN_SFF_MASK
2023-05-09 15:25:29 +00:00
case gotelem.CanEFFFrame:
idToWrite &= unix.CAN_EFF_MASK
idToWrite |= unix.CAN_EFF_FLAG
2023-05-09 15:25:29 +00:00
case gotelem.CanRTRFrame:
idToWrite |= unix.CAN_RTR_FLAG
2023-05-09 15:25:29 +00:00
case gotelem.CanErrFrame:
return errors.New("you can't send error frames")
2023-05-09 15:25:29 +00:00
default:
return errors.New("unknown frame type")
2023-04-14 20:42:16 +00:00
}
binary.LittleEndian.PutUint32(buf[:4], idToWrite)
// write the length, it's one byte, so do it directly.
payloadLength := len(msg.Data)
buf[4] = byte(payloadLength)
if payloadLength > 64 {
return fmt.Errorf("payload too large: %d", payloadLength)
2023-04-14 20:42:16 +00:00
}
// copy in the data now.
copy(buf[8:], msg.Data)
// send the buffer using unix syscalls!
var err error
if payloadLength > 8 {
err = unix.Send(sck.fd, buf, 0)
} else {
err = unix.Send(sck.fd, buf[:standardFrameSize], 0)
}
2023-04-14 20:42:16 +00:00
if err != nil {
return fmt.Errorf("error sending frame: %w", err)
}
return nil
}
func (sck *CanSocket) Recv() (*gotelem.Frame, error) {
2023-04-15 20:41:51 +00:00
// todo: support extended frames.
buf := make([]byte, fdFrameSize)
_, err := unix.Read(sck.fd, buf)
if err != nil {
return nil, err
}
id := binary.LittleEndian.Uint32(buf[0:4])
var k gotelem.Kind
if id&unix.CAN_EFF_FLAG != 0 {
// extended id frame
2023-05-09 15:25:29 +00:00
k = gotelem.CanEFFFrame
} else {
// it's a normal can frame
2023-05-09 15:25:29 +00:00
k = gotelem.CanSFFFrame
}
dataLength := uint8(buf[4])
result := &gotelem.Frame{
Id: id & unix.CAN_EFF_MASK,
Kind: k,
Data: buf[8 : dataLength+8],
}
return result, nil
2023-04-15 20:41:51 +00:00
2023-04-14 20:42:16 +00:00
}