gotelem/socketcan/socketcan.go
2023-04-15 00:01:36 -05:00

192 lines
3.8 KiB
Go

package socketcan
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"net"
"golang.org/x/sys/unix"
)
// this file implements a simple wrapper around linux socketCAN
type CanSocket struct {
iface *net.Interface
addr *unix.SockaddrCAN
fd int
}
type Frame struct {
ID uint32
Data []uint8
Kind Kind
}
//internal frame structure for socketcan with padding
type stdFrame struct {
ID uint32
Len uint8
_pad, _res1 uint8 // padding
Dlc uint8
Data [8]uint8
}
func Marshal(f Frame) (*bytes.Buffer, error) {
if len(f.Data) > 8 && f.Kind == SFF {
return nil, errors.New("data too large for std frame")
}
if len(f.Data) > 64 && f.Kind == EFF {
return nil, errors.New("data too large for extended frame")
}
var idflags uint32 = f.ID
if f.Kind == EFF {
idflags = idflags | unix.CAN_EFF_FLAG
} else if f.Kind == RTR {
idflags = idflags | unix.CAN_RTR_FLAG
} else if f.Kind == ERR {
idflags = idflags | unix.CAN_ERR_FLAG
}
var d [8]uint8
for i := 0; i < len(f.Data); i++ {
d[i] = f.Data[i]
}
var unixFrame stdFrame = stdFrame{
ID: idflags, Len: uint8(len(f.Data)),
Data: d,
}
// finally, write our bytes buffer.
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, unixFrame)
if err != nil {
return nil, err
}
return buf, nil
}
func Unmarshal(f *Frame, buf *bytes.Buffer) error {
return nil
}
//go:generate stringer -output=frame_kind.go -type Kind
type Kind uint8
const (
SFF Kind = iota // Standard Frame Format
EFF // Extended Frame
RTR // remote transmission requests
ERR // Error frame.
)
// helper function to make a filter.
// id and mask are straightforward, if inverted is true, the filter
// will reject anything that matches.
func MakeFilter(id, mask uint32, inverted bool) *unix.CanFilter {
f := &unix.CanFilter{Id: id, Mask: mask}
if inverted {
f.Id = f.Id | unix.CAN_INV_FILTER
}
return f
}
const standardFrameSize = unix.CAN_MTU
// the hack.
const extendedFrameSize = unix.CAN_MTU + 56
// Constructs a new CanSocket and creates a file descriptor for it.
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
}
// close the socket file descriptor, freeing it from the system.
func (sck *CanSocket) Close() error {
return unix.Close(sck.fd)
}
// get the name of the socket, or nil if it hasn't been bound yet.
func (sck *CanSocket) Name() string {
return sck.iface.Name
}
// should we log errors?
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)
if err != nil {
return err
}
return nil
}
func (sck *CanSocket) SetFilters(filters []unix.CanFilter) error {
return unix.SetsockoptCanRawFilter(sck.fd, unix.SOL_CAN_RAW, unix.CAN_RAW_FILTER, filters)
}
func (sck *CanSocket) Send(msg Frame) error {
// convert our abstract frame into a real unix frame and then push it.
// check return value to raise errors.
buf, err := Marshal(msg)
if err != nil {
return fmt.Errorf("error sending frame: %w", err)
}
if buf.Len() != unix.CAN_MTU {
return fmt.Errorf("socket send: buffer size mismatch %d", buf.Len())
}
// send the buffer using unix syscalls!
err = unix.Send(sck.fd, buf.Bytes(), 0)
if err != nil {
return fmt.Errorf("error sending frame: %w", err)
}
return nil
}
func (sck *CanSocket) Recv() (*Frame, error) {
return nil, errors.New("not implemented")
}