From 3c85be565c4e5cd649583c7fde7209f026078b82 Mon Sep 17 00:00:00 2001 From: hartkopp Date: Thu, 11 May 2006 09:19:16 +0000 Subject: [PATCH] added CAN error frame support. git-svn-id: svn://svn.berlios.de//socketcan/trunk@9 030b6a49-0b11-0410-94ab-b0dab22257f2 --- kernel/2.4/can/af_can.c | 24 +++- kernel/2.4/can/af_can.h | 3 + kernel/2.4/can/can.h | 1 + kernel/2.4/can/can_error.h | 125 +++++++++++++++++ kernel/2.4/can/proc.c | 37 +++++ kernel/2.4/can/raw.c | 68 ++++++++- kernel/2.4/can/raw.h | 4 +- kernel/2.6/include/linux/can/af_can.h | 3 + kernel/2.6/include/linux/can/can.h | 1 + kernel/2.6/include/linux/can/can_error.h | 125 +++++++++++++++++ kernel/2.6/include/linux/can/raw.h | 5 +- kernel/2.6/net/can/af_can.c | 24 +++- kernel/2.6/net/can/proc.c | 33 +++++ kernel/2.6/net/can/raw.c | 68 ++++++++- test/Makefile | 1 + test/tst-err.c | 167 +++++++++++++++++++++++ 16 files changed, 670 insertions(+), 19 deletions(-) create mode 100644 kernel/2.4/can/can_error.h create mode 100644 kernel/2.6/include/linux/can/can_error.h create mode 100644 test/tst-err.c diff --git a/kernel/2.4/can/af_can.c b/kernel/2.4/can/af_can.c index 4997ccc..be8be52 100644 --- a/kernel/2.4/can/af_can.c +++ b/kernel/2.4/can/af_can.c @@ -544,11 +544,23 @@ static int can_rcv_filter(struct rcv_dev_list *q, struct sk_buff *skb) if (q->entries == 0) return 0; + if (can_id & CAN_ERR_FLAG) { + /* check for error frame entries only */ + for (p = q->rx_err; p; p = p->next) { + if (can_id & p->mask) { + DBG("match on rx_err skbuff %p\n", skb); + deliver(skb, p); + matches++; + } + } + goto out; + } + /* check for unfiltered entries */ for (p = q->rx_all; p; p = p->next) { - DBG("match on rx_all skbuff %p\n", skb); - deliver(skb, p); - matches++; + DBG("match on rx_all skbuff %p\n", skb); + deliver(skb, p); + matches++; } /* check for can_id/mask entries */ @@ -586,6 +598,7 @@ static int can_rcv_filter(struct rcv_dev_list *q, struct sk_buff *skb) } } +out: return matches; } @@ -594,6 +607,8 @@ static struct rcv_list **find_rcv_list(canid_t *can_id, canid_t *mask, struct ne canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking values */ canid_t eff = *can_id & *mask & CAN_EFF_FLAG; /* correct EFF check? */ canid_t rtr = *can_id & *mask & CAN_RTR_FLAG; /* correct RTR check? */ + canid_t err = *mask & CAN_ERR_FLAG; /* mask for error frames only */ + struct rcv_dev_list *p; /* make some paranoic operations */ @@ -637,6 +652,9 @@ static struct rcv_list **find_rcv_list(canid_t *can_id, canid_t *mask, struct ne } } + if (err) /* error frames */ + return &p->rx_err; + if (inv) /* inverse can_id/can_mask filter and RTR */ return &p->rx_inv; diff --git a/kernel/2.4/can/af_can.h b/kernel/2.4/can/af_can.h index ed1011b..70c9488 100644 --- a/kernel/2.4/can/af_can.h +++ b/kernel/2.4/can/af_can.h @@ -85,6 +85,8 @@ struct sockaddr_can { } can_addr; }; +typedef canid_t can_err_mask_t; + struct can_filter { canid_t can_id; canid_t can_mask; @@ -128,6 +130,7 @@ struct rcv_list { struct rcv_dev_list { struct rcv_dev_list *next; struct net_device *dev; + struct rcv_list *rx_err; struct rcv_list *rx_all; struct rcv_list *rx_fil; struct rcv_list *rx_inv; diff --git a/kernel/2.4/can/can.h b/kernel/2.4/can/can.h index 9497a8a..36338a6 100644 --- a/kernel/2.4/can/can.h +++ b/kernel/2.4/can/can.h @@ -57,6 +57,7 @@ RCSID("$Id$"); /* special address description flags for the CAN_ID */ #define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */ #define CAN_RTR_FLAG 0x40000000U /* remote transmission request */ +#define CAN_ERR_FLAG 0x20000000U /* error frame */ /* valid bits in CAN ID for frame formats */ #define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */ diff --git a/kernel/2.4/can/can_error.h b/kernel/2.4/can/can_error.h new file mode 100644 index 0000000..e337349 --- /dev/null +++ b/kernel/2.4/can/can_error.h @@ -0,0 +1,125 @@ +/* + * can_error_h + * + * Copyright (c) 2002-2005 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, the following disclaimer and + * the referenced file 'COPYING'. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2 as distributed in the 'COPYING' + * file from the main directory of the linux kernel source. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to + * + */ + +#ifndef CAN_ERROR_H +#define CAN_ERROR_H + +#ifdef __KERNEL__ +#include "version.h" +RCSID("$Id$"); +#endif + +#define CAN_ERR_DLC 8 /* dlc for error frames */ + +/* error class (mask) in can_id */ +#define CAN_ERR_BUSOFF 0x00000001U /* bus off */ +#define CAN_ERR_TX_TIMEOUT 0x00000002U /* TX timeout (netdevice driver) */ +#define CAN_ERR_ACK 0x00000004U /* received no ACK on transmission */ +#define CAN_ERR_LOSTARB 0x00000008U /* lost arbitration / data[0] */ +#define CAN_ERR_CRTL 0x00000010U /* controller problems / data[1] */ +#define CAN_ERR_PROT 0x00000020U /* protocol violations / data[2..3] */ +#define CAN_ERR_TRX 0x00000040U /* transceiver status / data[4] */ + +#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */ + +/* arbitration lost in bit ... / data[0] */ +#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */ + /* else bit number in bitstream */ + +/* error status of CAN-controller / data[1] */ +#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */ +#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */ +#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */ +#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */ +#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */ + +/* error in CAN protocol (type) / data[2] */ +#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */ +#define CAN_ERR_PROT_BIT 0x01 /* single bit error */ +#define CAN_ERR_PROT_FORM 0x02 /* frame format error */ +#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */ +#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */ +#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */ +#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */ +#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */ +#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */ + +/* error in CAN protocol (location) / data[3] */ +#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */ +#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */ +#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */ +#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/ +#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */ +#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */ +#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */ +#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */ +#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */ +#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */ +#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */ +#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */ +#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */ +#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */ +#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */ +#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */ +#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */ +#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */ +#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */ +#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */ + +/* error status of CAN-transceiver / data[4] */ +/* CANH CANL */ +#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */ +#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */ +#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */ +#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */ +#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */ +#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */ +#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */ +#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */ +#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */ +#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */ + +/* controller specific additional information / data[5..7] */ + +#endif /* CAN_ERROR_H */ diff --git a/kernel/2.4/can/proc.c b/kernel/2.4/can/proc.c index 76e5630..20ac90c 100644 --- a/kernel/2.4/can/proc.c +++ b/kernel/2.4/can/proc.c @@ -60,6 +60,7 @@ RCSID("$Id$"); #define CAN_PROC_RCVLIST_INV "rcvlist_inv" #define CAN_PROC_RCVLIST_SFF "rcvlist_sff" #define CAN_PROC_RCVLIST_EFF "rcvlist_eff" +#define CAN_PROC_RCVLIST_ERR "rcvlist_err" static void can_init_stats(int caller); static void can_stat_update(unsigned long data); @@ -76,6 +77,7 @@ static int can_proc_read_rcvlist_fil(char *page, char **start, off_t off, int co static int can_proc_read_rcvlist_inv(char *page, char **start, off_t off, int count, int *eof, void *data); static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off, int count, int *eof, void *data); static int can_proc_read_rcvlist_eff(char *page, char **start, off_t off, int count, int *eof, void *data); +static int can_proc_read_rcvlist_err(char *page, char **start, off_t off, int count, int *eof, void *data); static struct proc_dir_entry *can_dir = NULL; static struct proc_dir_entry *pde_version = NULL; @@ -86,6 +88,7 @@ static struct proc_dir_entry *pde_rcvlist_fil = NULL; static struct proc_dir_entry *pde_rcvlist_inv = NULL; static struct proc_dir_entry *pde_rcvlist_sff = NULL; static struct proc_dir_entry *pde_rcvlist_eff = NULL; +static struct proc_dir_entry *pde_rcvlist_err = NULL; struct timer_list stattimer; /* timer for statistics update */ @@ -120,6 +123,7 @@ void can_init_proc(void) pde_rcvlist_inv = can_create_proc_read_entry(CAN_PROC_RCVLIST_INV, 0644, can_proc_read_rcvlist_inv, NULL); pde_rcvlist_sff = can_create_proc_read_entry(CAN_PROC_RCVLIST_SFF, 0644, can_proc_read_rcvlist_sff, NULL); pde_rcvlist_eff = can_create_proc_read_entry(CAN_PROC_RCVLIST_EFF, 0644, can_proc_read_rcvlist_eff, NULL); + pde_rcvlist_err = can_create_proc_read_entry(CAN_PROC_RCVLIST_ERR, 0644, can_proc_read_rcvlist_err, NULL); if (stats_timer) { /* the statistics are updated every second (timer triggered) */ @@ -167,6 +171,10 @@ void can_remove_proc(void) can_remove_proc_entry(CAN_PROC_RCVLIST_EFF); } + if (pde_rcvlist_err) { + can_remove_proc_entry(CAN_PROC_RCVLIST_ERR); + } + if (can_dir) { remove_proc_entry(CAN_PROC_DIR, NULL); } @@ -458,6 +466,35 @@ int can_proc_read_rcvlist_eff(char *page, char **start, off_t off, int count, in return len; } +int can_proc_read_rcvlist_err(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + struct rcv_dev_list *p; + + MOD_INC_USE_COUNT; + + /* RX_ERR */ + len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_err':\n"); + + /* find receive list for this device */ + for (p = rx_dev_list; p; p = p->next) { + + if (p->rx_err) { + len = can_print_recv_banner(page, len); + len = can_print_recv_list(page, len, p->rx_err, p->dev); + } else + if (p->dev) + len += snprintf(page + len, PAGE_SIZE - len, " (%s: no entry)\n", p->dev->name); + } + + len += snprintf(page + len, PAGE_SIZE - len, "\n"); + + MOD_DEC_USE_COUNT; + + *eof = 1; + return len; +} + /**************************************************/ /* proc utility functions */ /**************************************************/ diff --git a/kernel/2.4/can/raw.c b/kernel/2.4/can/raw.c index 8a6cf0e..a285d0d 100644 --- a/kernel/2.4/can/raw.c +++ b/kernel/2.4/can/raw.c @@ -51,6 +51,7 @@ #include #include "af_can.h" +#include "can_error.h" #include "version.h" #include "raw.h" @@ -106,10 +107,12 @@ struct canraw_opt { int ifindex; int count; struct can_filter *filter; + can_err_mask_t err_mask; }; #define canraw_sk(sk) ((struct canraw_opt *)&(sk)->tp_pinfo) +#define MASK_ALL 0 static struct proto_ops raw_ops = { .family = PF_CAN, @@ -160,7 +163,11 @@ static int raw_release(struct socket *sock) raw_remove_filters(dev, sk); kfree(canraw_sk(sk)->filter); } else if (canraw_sk(sk)->bound) - can_rx_unregister(dev, 0, 0, raw_rcv, sk); + can_rx_unregister(dev, 0, MASK_ALL, raw_rcv, sk); + + /* remove current error mask */ + if (canraw_sk(sk)->err_mask && canraw_sk(sk)->bound) + can_rx_unregister(dev, 0, (canid_t)(canraw_sk(sk)->err_mask | CAN_ERR_FLAG), raw_rcv, sk); if (dev) { can_dev_unregister(dev, raw_notifier, sk); @@ -196,7 +203,7 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len) if (canraw_sk(sk)->count > 0) { raw_remove_filters(sk); } else { - can_rx_unregister(dev, 0, 0, raw_rcv, sk); + can_rx_unregister(dev, 0, MASK_ALL, raw_rcv, sk); } if (dev) dev_put(dev); @@ -224,7 +231,10 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len) if (canraw_sk(sk)->count > 0) /* filters set by setsockopt */ raw_add_filters(dev, sk); else - can_rx_register(dev, 0, 0, raw_rcv, sk, IDENT); + can_rx_register(dev, 0, MASK_ALL, raw_rcv, sk, IDENT); + + if (canraw_sk(sk)->err_mask) /* error frame filter set by setsockopt */ + can_rx_register(dev, 0, (canid_t)(canraw_sk(sk)->err_mask | CAN_ERR_FLAG), raw_rcv, sk, IDENT); canraw_sk(sk)->bound = 1; @@ -268,6 +278,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, struct sock *sk = sock->sk; struct can_filter *filter = NULL; struct net_device *dev = NULL; + can_err_mask_t err_mask = 0; int count = 0; int err; @@ -301,7 +312,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, canraw_sk(sk)->count = 0; canraw_sk(sk)->filter = NULL; } else if (canraw_sk(sk)->bound) - can_rx_unregister(dev, 0, 0, raw_rcv, sk); + can_rx_unregister(dev, 0, MASK_ALL, raw_rcv, sk); /* add new filters & register */ if (optlen) { @@ -310,7 +321,37 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, if (canraw_sk(sk)->bound) raw_add_filters(dev, sk); } else if (canraw_sk(sk)->bound) - can_rx_register(dev, 0, 0, raw_rcv, sk, IDENT); + can_rx_register(dev, 0, MASK_ALL, raw_rcv, sk, IDENT); + + if (dev) + dev_put(dev); + + break; + + case CAN_RAW_ERR_FILTER: + if (optlen) { + if (optlen != sizeof(err_mask)) + return -EINVAL; + if (err = copy_from_user(&err_mask, optval, optlen)) { + return err; + } + } + + err_mask &= CAN_ERR_MASK; + + if (canraw_sk(sk)->bound && canraw_sk(sk)->ifindex) + dev = dev_get_by_index(canraw_sk(sk)->ifindex); + + /* remove current error mask */ + if (canraw_sk(sk)->err_mask && canraw_sk(sk)->bound) + can_rx_unregister(dev, 0, (canid_t)(canraw_sk(sk)->err_mask | CAN_ERR_FLAG), raw_rcv, sk); + + /* add new error mask */ + if (optlen) { + canraw_sk(sk)->err_mask = err_mask; + if (canraw_sk(sk)->err_mask & canraw_sk(sk)->bound) + can_rx_register(dev, 0, (canid_t)(canraw_sk(sk)->err_mask | CAN_ERR_FLAG), raw_rcv, sk, IDENT); + } if (dev) dev_put(dev); @@ -352,6 +393,23 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, return -EFAULT; break; + case CAN_RAW_ERR_FILTER: + if (get_user(len, optlen)) + return -EFAULT; + + if (len < sizeof(can_err_mask_t)) + return -EINVAL; + + if (len > sizeof(can_err_mask_t)) + len = sizeof(can_err_mask_t); + + if (copy_to_user(optval, &canraw_sk(sk)->err_mask, len)) + return -EFAULT; + + if (put_user(len, optlen)) + return -EFAULT; + break; + default: return -ENOPROTOOPT; } diff --git a/kernel/2.4/can/raw.h b/kernel/2.4/can/raw.h index f8ccc19..3a8d035 100644 --- a/kernel/2.4/can/raw.h +++ b/kernel/2.4/can/raw.h @@ -54,6 +54,8 @@ RCSID("$Id$"); #define SOL_CAN_RAW (SOL_CAN_BASE + CAN_RAW) -#define CAN_RAW_FILTER 1 + +#define CAN_RAW_FILTER 1 +#define CAN_RAW_ERR_FILTER 2 #endif diff --git a/kernel/2.6/include/linux/can/af_can.h b/kernel/2.6/include/linux/can/af_can.h index 6da01dd..cf9a0c4 100644 --- a/kernel/2.6/include/linux/can/af_can.h +++ b/kernel/2.6/include/linux/can/af_can.h @@ -86,6 +86,8 @@ struct sockaddr_can { } can_addr; }; +typedef canid_t can_err_mask_t; + struct can_filter { canid_t can_id; canid_t can_mask; @@ -142,6 +144,7 @@ struct rcv_list { struct rcv_dev_list { struct rcv_dev_list *next; struct net_device *dev; + struct rcv_list *rx_err; struct rcv_list *rx_all; struct rcv_list *rx_fil; struct rcv_list *rx_inv; diff --git a/kernel/2.6/include/linux/can/can.h b/kernel/2.6/include/linux/can/can.h index 9497a8a..36338a6 100644 --- a/kernel/2.6/include/linux/can/can.h +++ b/kernel/2.6/include/linux/can/can.h @@ -57,6 +57,7 @@ RCSID("$Id$"); /* special address description flags for the CAN_ID */ #define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */ #define CAN_RTR_FLAG 0x40000000U /* remote transmission request */ +#define CAN_ERR_FLAG 0x20000000U /* error frame */ /* valid bits in CAN ID for frame formats */ #define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */ diff --git a/kernel/2.6/include/linux/can/can_error.h b/kernel/2.6/include/linux/can/can_error.h new file mode 100644 index 0000000..e337349 --- /dev/null +++ b/kernel/2.6/include/linux/can/can_error.h @@ -0,0 +1,125 @@ +/* + * can_error_h + * + * Copyright (c) 2002-2005 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, the following disclaimer and + * the referenced file 'COPYING'. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2 as distributed in the 'COPYING' + * file from the main directory of the linux kernel source. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to + * + */ + +#ifndef CAN_ERROR_H +#define CAN_ERROR_H + +#ifdef __KERNEL__ +#include "version.h" +RCSID("$Id$"); +#endif + +#define CAN_ERR_DLC 8 /* dlc for error frames */ + +/* error class (mask) in can_id */ +#define CAN_ERR_BUSOFF 0x00000001U /* bus off */ +#define CAN_ERR_TX_TIMEOUT 0x00000002U /* TX timeout (netdevice driver) */ +#define CAN_ERR_ACK 0x00000004U /* received no ACK on transmission */ +#define CAN_ERR_LOSTARB 0x00000008U /* lost arbitration / data[0] */ +#define CAN_ERR_CRTL 0x00000010U /* controller problems / data[1] */ +#define CAN_ERR_PROT 0x00000020U /* protocol violations / data[2..3] */ +#define CAN_ERR_TRX 0x00000040U /* transceiver status / data[4] */ + +#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */ + +/* arbitration lost in bit ... / data[0] */ +#define CAN_ERR_LOSTARB_UNSPEC 0x00 /* unspecified */ + /* else bit number in bitstream */ + +/* error status of CAN-controller / data[1] */ +#define CAN_ERR_CRTL_UNSPEC 0x00 /* unspecified */ +#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */ +#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */ +#define CAN_ERR_CRTL_RX_WARNING 0x04 /* reached warning level for RX errors */ +#define CAN_ERR_CRTL_TX_WARNING 0x08 /* reached warning level for TX errors */ + +/* error in CAN protocol (type) / data[2] */ +#define CAN_ERR_PROT_UNSPEC 0x00 /* unspecified */ +#define CAN_ERR_PROT_BIT 0x01 /* single bit error */ +#define CAN_ERR_PROT_FORM 0x02 /* frame format error */ +#define CAN_ERR_PROT_STUFF 0x04 /* bit stuffing error */ +#define CAN_ERR_PROT_BIT0 0x08 /* unable to send dominant bit */ +#define CAN_ERR_PROT_BIT1 0x10 /* unable to send recessive bit */ +#define CAN_ERR_PROT_OVERLOAD 0x20 /* bus overload */ +#define CAN_ERR_PROT_ACTIVE 0x40 /* active error announcement */ +#define CAN_ERR_PROT_TX 0x80 /* error occured on transmission */ + +/* error in CAN protocol (location) / data[3] */ +#define CAN_ERR_PROT_LOC_UNSPEC 0x00 /* unspecified */ +#define CAN_ERR_PROT_LOC_SOF 0x03 /* start of frame */ +#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */ +#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/ +#define CAN_ERR_PROT_LOC_SRTR 0x04 /* substitute RTR (SFF: RTR) */ +#define CAN_ERR_PROT_LOC_IDE 0x05 /* identifier extension */ +#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */ +#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */ +#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */ +#define CAN_ERR_PROT_LOC_RTR 0x0C /* RTR */ +#define CAN_ERR_PROT_LOC_RES1 0x0D /* reserved bit 1 */ +#define CAN_ERR_PROT_LOC_RES0 0x09 /* reserved bit 0 */ +#define CAN_ERR_PROT_LOC_DLC 0x0B /* data length code */ +#define CAN_ERR_PROT_LOC_DATA 0x0A /* data section */ +#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */ +#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */ +#define CAN_ERR_PROT_LOC_ACK 0x19 /* ACK slot */ +#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */ +#define CAN_ERR_PROT_LOC_EOF 0x1A /* end of frame */ +#define CAN_ERR_PROT_LOC_INTERM 0x12 /* intermission */ + +/* error status of CAN-transceiver / data[4] */ +/* CANH CANL */ +#define CAN_ERR_TRX_UNSPEC 0x00 /* 0000 0000 */ +#define CAN_ERR_TRX_CANH_NO_WIRE 0x04 /* 0000 0100 */ +#define CAN_ERR_TRX_CANH_SHORT_TO_BAT 0x05 /* 0000 0101 */ +#define CAN_ERR_TRX_CANH_SHORT_TO_VCC 0x06 /* 0000 0110 */ +#define CAN_ERR_TRX_CANH_SHORT_TO_GND 0x07 /* 0000 0111 */ +#define CAN_ERR_TRX_CANL_NO_WIRE 0x40 /* 0100 0000 */ +#define CAN_ERR_TRX_CANL_SHORT_TO_BAT 0x50 /* 0101 0000 */ +#define CAN_ERR_TRX_CANL_SHORT_TO_VCC 0x60 /* 0110 0000 */ +#define CAN_ERR_TRX_CANL_SHORT_TO_GND 0x70 /* 0111 0000 */ +#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */ + +/* controller specific additional information / data[5..7] */ + +#endif /* CAN_ERROR_H */ diff --git a/kernel/2.6/include/linux/can/raw.h b/kernel/2.6/include/linux/can/raw.h index 7d55ddc..458d644 100644 --- a/kernel/2.6/include/linux/can/raw.h +++ b/kernel/2.6/include/linux/can/raw.h @@ -52,8 +52,9 @@ RCSID("$Id$"); #include - #define SOL_CAN_RAW (SOL_CAN_BASE + CAN_RAW) -#define CAN_RAW_FILTER 1 + +#define CAN_RAW_FILTER 1 +#define CAN_RAW_ERR_FILTER 2 #endif diff --git a/kernel/2.6/net/can/af_can.c b/kernel/2.6/net/can/af_can.c index 3f59e1f..67a9102 100644 --- a/kernel/2.6/net/can/af_can.c +++ b/kernel/2.6/net/can/af_can.c @@ -582,11 +582,23 @@ static int can_rcv_filter(struct rcv_dev_list *q, struct sk_buff *skb) if (q->entries == 0) return 0; + if (can_id & CAN_ERR_FLAG) { + /* check for error frame entries only */ + for (p = q->rx_err; p; p = p->next) { + if (can_id & p->mask) { + DBG("match on rx_err skbuff %p\n", skb); + deliver(skb, p); + matches++; + } + } + goto out; + } + /* check for unfiltered entries */ for (p = q->rx_all; p; p = p->next) { - DBG("match on rx_all skbuff %p\n", skb); - deliver(skb, p); - matches++; + DBG("match on rx_all skbuff %p\n", skb); + deliver(skb, p); + matches++; } /* check for can_id/mask entries */ @@ -624,6 +636,7 @@ static int can_rcv_filter(struct rcv_dev_list *q, struct sk_buff *skb) } } +out: return matches; } @@ -632,6 +645,8 @@ static struct rcv_list **find_rcv_list(canid_t *can_id, canid_t *mask, struct ne canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking values */ canid_t eff = *can_id & *mask & CAN_EFF_FLAG; /* correct EFF check? */ canid_t rtr = *can_id & *mask & CAN_RTR_FLAG; /* correct RTR check? */ + canid_t err = *mask & CAN_ERR_FLAG; /* mask for error frames only */ + struct rcv_dev_list *p; /* make some paranoic operations */ @@ -675,6 +690,9 @@ static struct rcv_list **find_rcv_list(canid_t *can_id, canid_t *mask, struct ne } } + if (err) /* error frames */ + return &p->rx_err; + if (inv) /* inverse can_id/can_mask filter and RTR */ return &p->rx_inv; diff --git a/kernel/2.6/net/can/proc.c b/kernel/2.6/net/can/proc.c index c8cbca2..05f5128 100644 --- a/kernel/2.6/net/can/proc.c +++ b/kernel/2.6/net/can/proc.c @@ -61,6 +61,7 @@ RCSID("$Id$"); #define CAN_PROC_RCVLIST_INV "rcvlist_inv" #define CAN_PROC_RCVLIST_SFF "rcvlist_sff" #define CAN_PROC_RCVLIST_EFF "rcvlist_eff" +#define CAN_PROC_RCVLIST_ERR "rcvlist_err" static void can_init_stats(int caller); static void can_stat_update(unsigned long data); @@ -77,6 +78,7 @@ static int can_proc_read_rcvlist_fil(char *page, char **start, off_t off, int co static int can_proc_read_rcvlist_inv(char *page, char **start, off_t off, int count, int *eof, void *data); static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off, int count, int *eof, void *data); static int can_proc_read_rcvlist_eff(char *page, char **start, off_t off, int count, int *eof, void *data); +static int can_proc_read_rcvlist_err(char *page, char **start, off_t off, int count, int *eof, void *data); static struct proc_dir_entry *can_dir = NULL; static struct proc_dir_entry *pde_version = NULL; @@ -87,6 +89,7 @@ static struct proc_dir_entry *pde_rcvlist_fil = NULL; static struct proc_dir_entry *pde_rcvlist_inv = NULL; static struct proc_dir_entry *pde_rcvlist_sff = NULL; static struct proc_dir_entry *pde_rcvlist_eff = NULL; +static struct proc_dir_entry *pde_rcvlist_err = NULL; struct timer_list stattimer; /* timer for statistics update */ @@ -121,6 +124,7 @@ void can_init_proc(void) pde_rcvlist_inv = can_create_proc_read_entry(CAN_PROC_RCVLIST_INV, 0644, can_proc_read_rcvlist_inv, NULL); pde_rcvlist_sff = can_create_proc_read_entry(CAN_PROC_RCVLIST_SFF, 0644, can_proc_read_rcvlist_sff, NULL); pde_rcvlist_eff = can_create_proc_read_entry(CAN_PROC_RCVLIST_EFF, 0644, can_proc_read_rcvlist_eff, NULL); + pde_rcvlist_err = can_create_proc_read_entry(CAN_PROC_RCVLIST_ERR, 0644, can_proc_read_rcvlist_err, NULL); if (stats_timer) { /* the statistics are updated every second (timer triggered) */ @@ -168,6 +172,10 @@ void can_remove_proc(void) can_remove_proc_entry(CAN_PROC_RCVLIST_EFF); } + if (pde_rcvlist_err) { + can_remove_proc_entry(CAN_PROC_RCVLIST_ERR); + } + if (can_dir) { remove_proc_entry(CAN_PROC_DIR, NULL); } @@ -427,6 +435,31 @@ int can_proc_read_rcvlist_eff(char *page, char **start, off_t off, int count, in return len; } +int can_proc_read_rcvlist_err(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + struct rcv_dev_list *p; + + /* RX_ERR */ + len += snprintf(page + len, PAGE_SIZE - len, "\nreceive list 'rx_err':\n"); + + /* find receive list for this device */ + for (p = rx_dev_list; p; p = p->next) { + + if (p->rx_err) { + len = can_print_recv_banner(page, len); + len = can_print_recv_list(page, len, p->rx_err, p->dev); + } else + if (p->dev) + len += snprintf(page + len, PAGE_SIZE - len, " (%s: no entry)\n", p->dev->name); + } + + len += snprintf(page + len, PAGE_SIZE - len, "\n"); + + *eof = 1; + return len; +} + /**************************************************/ /* proc utility functions */ /**************************************************/ diff --git a/kernel/2.6/net/can/raw.c b/kernel/2.6/net/can/raw.c index 8411e22..c5d196e 100644 --- a/kernel/2.6/net/can/raw.c +++ b/kernel/2.6/net/can/raw.c @@ -52,6 +52,7 @@ #include #include +#include #include #include "version.h" @@ -126,6 +127,7 @@ struct raw_opt { int ifindex; int count; struct can_filter *filter; + can_err_mask_t err_mask; }; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13) @@ -160,6 +162,7 @@ static struct can_proto raw_can_proto = { #endif +#define MASK_ALL 0 static __init int raw_init(void) { @@ -191,7 +194,11 @@ static int raw_release(struct socket *sock) raw_remove_filters(dev, sk); kfree(canraw_sk(sk)->filter); } else if (canraw_sk(sk)->bound) - can_rx_unregister(dev, 0, 0, raw_rcv, sk); + can_rx_unregister(dev, 0, MASK_ALL, raw_rcv, sk); + + /* remove current error mask */ + if (canraw_sk(sk)->err_mask && canraw_sk(sk)->bound) + can_rx_unregister(dev, 0, (canid_t)(canraw_sk(sk)->err_mask | CAN_ERR_FLAG), raw_rcv, sk); if (dev) { can_dev_unregister(dev, raw_notifier, sk); @@ -227,7 +234,7 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len) if (canraw_sk(sk)->count > 0) { raw_remove_filters(sk); } else { - can_rx_unregister(dev, 0, 0, raw_rcv, sk); + can_rx_unregister(dev, 0, MASK_ALL, raw_rcv, sk); } if (dev) dev_put(dev); @@ -255,7 +262,10 @@ static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len) if (canraw_sk(sk)->count > 0) /* filters set by setsockopt */ raw_add_filters(dev, sk); else - can_rx_register(dev, 0, 0, raw_rcv, sk, IDENT); + can_rx_register(dev, 0, MASK_ALL, raw_rcv, sk, IDENT); + + if (canraw_sk(sk)->err_mask) /* error frame filter set by setsockopt */ + can_rx_register(dev, 0, (canid_t)(canraw_sk(sk)->err_mask | CAN_ERR_FLAG), raw_rcv, sk, IDENT); canraw_sk(sk)->bound = 1; @@ -299,6 +309,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, struct sock *sk = sock->sk; struct can_filter *filter = NULL; struct net_device *dev = NULL; + can_err_mask_t err_mask = 0; int count = 0; int err; @@ -332,7 +343,7 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, canraw_sk(sk)->count = 0; canraw_sk(sk)->filter = NULL; } else if (canraw_sk(sk)->bound) - can_rx_unregister(dev, 0, 0, raw_rcv, sk); + can_rx_unregister(dev, 0, MASK_ALL, raw_rcv, sk); /* add new filters & register */ if (optlen) { @@ -341,7 +352,37 @@ static int raw_setsockopt(struct socket *sock, int level, int optname, if (canraw_sk(sk)->bound) raw_add_filters(dev, sk); } else if (canraw_sk(sk)->bound) - can_rx_register(dev, 0, 0, raw_rcv, sk, IDENT); + can_rx_register(dev, 0, MASK_ALL, raw_rcv, sk, IDENT); + + if (dev) + dev_put(dev); + + break; + + case CAN_RAW_ERR_FILTER: + if (optlen) { + if (optlen != sizeof(err_mask)) + return -EINVAL; + if (err = copy_from_user(&err_mask, optval, optlen)) { + return err; + } + } + + err_mask &= CAN_ERR_MASK; + + if (canraw_sk(sk)->bound && canraw_sk(sk)->ifindex) + dev = dev_get_by_index(canraw_sk(sk)->ifindex); + + /* remove current error mask */ + if (canraw_sk(sk)->err_mask && canraw_sk(sk)->bound) + can_rx_unregister(dev, 0, (canid_t)(canraw_sk(sk)->err_mask | CAN_ERR_FLAG), raw_rcv, sk); + + /* add new error mask */ + if (optlen) { + canraw_sk(sk)->err_mask = err_mask; + if (canraw_sk(sk)->err_mask & canraw_sk(sk)->bound) + can_rx_register(dev, 0, (canid_t)(canraw_sk(sk)->err_mask | CAN_ERR_FLAG), raw_rcv, sk, IDENT); + } if (dev) dev_put(dev); @@ -383,6 +424,23 @@ static int raw_getsockopt(struct socket *sock, int level, int optname, return -EFAULT; break; + case CAN_RAW_ERR_FILTER: + if (get_user(len, optlen)) + return -EFAULT; + + if (len < sizeof(can_err_mask_t)) + return -EINVAL; + + if (len > sizeof(can_err_mask_t)) + len = sizeof(can_err_mask_t); + + if (copy_to_user(optval, &canraw_sk(sk)->err_mask, len)) + return -EFAULT; + + if (put_user(len, optlen)) + return -EFAULT; + break; + default: return -ENOPROTOOPT; } diff --git a/test/Makefile b/test/Makefile index 2cabf0c..6e2cb0b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -44,6 +44,7 @@ CFLAGS = -O2 -Wall -Wno-parentheses -I../kernel/2.4/can -fno-strict-aliasing PROGRAMS_GPL = tst-raw \ tst-raw-filter \ + tst-err \ tst-raw-sendto \ tst-bcm-cycle \ tst-bcm-tx_read \ diff --git a/test/tst-err.c b/test/tst-err.c new file mode 100644 index 0000000..fbf0b45 --- /dev/null +++ b/test/tst-err.c @@ -0,0 +1,167 @@ +/* + * $Id$ + */ + +/* + * tst-err.c + * + * Copyright (c) 2002-2006 Volkswagen Group Electronic Research + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, the following disclaimer and + * the referenced file 'COPYING'. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2 as distributed in the 'COPYING' + * file from the main directory of the linux kernel source. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Send feedback to + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "af_can.h" +#include "raw.h" +#include "can_error.h" + +int main(int argc, char **argv) +{ + int s; + struct sockaddr_can addr; + struct can_filter rfilter; + struct can_frame frame; + can_err_mask_t err_mask = CAN_ERR_MASK; /* all */ + int nbytes; + struct ifreq ifr; + char *ifname = "vcan2"; + int ifindex; + int opt; + struct timeval tv; + + while ((opt = getopt(argc, argv, "i:m:")) != -1) { + switch (opt) { + case 'i': + ifname = optarg; + break; + case 'm': + err_mask = strtoul(optarg, (char **)NULL, 16); + break; + default: + fprintf(stderr, "Unknown option %c\n", opt); + break; + } + } + + + if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + perror("socket"); + return 1; + } + + rfilter.can_id = CAN_INV_FILTER; /* no normal CAN frames */ + rfilter.can_mask = 0; /* all: INV(all) == nothing */ + setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter)); + + setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER, &err_mask, sizeof(err_mask)); + + strcpy(ifr.ifr_name, ifname); + ioctl(s, SIOCGIFINDEX, &ifr); + ifindex = ifr.ifr_ifindex; + + addr.can_family = AF_CAN; + addr.can_ifindex = ifindex; + + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + return 1; + } + + while (1) { + + if ((nbytes = read(s, &frame, sizeof(struct can_frame))) < 0) { + perror("read"); + return 1; + } else if (nbytes < sizeof(struct can_frame)) { + fprintf(stderr, "read: incomplete CAN frame\n"); + return 1; + } else { + if (ioctl(s, SIOCGSTAMP, &tv) < 0) + perror("SIOCGSTAMP"); + else + printf("(%ld.%06ld) ", tv.tv_sec, tv.tv_usec); + + if (frame.can_id & CAN_ERR_BUSOFF) + printf("(bus off) "); + + if (frame.can_id & CAN_ERR_TX_TIMEOUT) + printf("(tx timeout) "); + + if (frame.can_id & CAN_ERR_ACK) + printf("(ack) "); + + if (frame.can_id & CAN_ERR_LOSTARB) { + printf("(lost arb)"); + if (frame.data[0]) + printf("[%d]", frame.data[0]); + printf(" "); + } + + if (frame.can_id & CAN_ERR_CRTL) { + printf("(crtl)"); + if (frame.data[1] & CAN_ERR_CRTL_RX_OVERFLOW) + printf("[RX buffer overflow]"); + if (frame.data[1] & CAN_ERR_CRTL_TX_OVERFLOW) + printf("[TX buffer overflow]"); + if (frame.data[1] & CAN_ERR_CRTL_RX_WARNING) + printf("[RX warning]"); + if (frame.data[1] & CAN_ERR_CRTL_TX_WARNING) + printf("[TX warning]"); + printf(" "); + } + + /* to be continued */ + + printf("\n"); + fflush(stdout); + } + } + + close(s); + + return 0; +} -- 2.39.2