]> rtime.felk.cvut.cz Git - socketcan-devel.git/commitdiff
added CAN error frame support.
authorhartkopp <hartkopp@030b6a49-0b11-0410-94ab-b0dab22257f2>
Thu, 11 May 2006 09:19:16 +0000 (09:19 +0000)
committerhartkopp <hartkopp@030b6a49-0b11-0410-94ab-b0dab22257f2>
Thu, 11 May 2006 09:19:16 +0000 (09:19 +0000)
git-svn-id: svn://svn.berlios.de//socketcan/trunk@9 030b6a49-0b11-0410-94ab-b0dab22257f2

16 files changed:
kernel/2.4/can/af_can.c
kernel/2.4/can/af_can.h
kernel/2.4/can/can.h
kernel/2.4/can/can_error.h [new file with mode: 0644]
kernel/2.4/can/proc.c
kernel/2.4/can/raw.c
kernel/2.4/can/raw.h
kernel/2.6/include/linux/can/af_can.h
kernel/2.6/include/linux/can/can.h
kernel/2.6/include/linux/can/can_error.h [new file with mode: 0644]
kernel/2.6/include/linux/can/raw.h
kernel/2.6/net/can/af_can.c
kernel/2.6/net/can/proc.c
kernel/2.6/net/can/raw.c
test/Makefile
test/tst-err.c [new file with mode: 0644]

index 4997ccc9e540ae6db9892301b2c7796924e7f033..be8be526a9ef8b12c6fbdf92202b332c87b33803 100644 (file)
@@ -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;
 
index ed1011b231be715c73e23fcbdb70a3f6342595cb..70c94887573629d674e72ef7e9c960383f06d0dc 100644 (file)
@@ -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;
index 9497a8a7e489a49f5843f6d3566ee1136d72433d..36338a69b1a447be47203ea333b5e161947a0e23 100644 (file)
@@ -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 (file)
index 0000000..e337349
--- /dev/null
@@ -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 <llcf@volkswagen.de>
+ *
+ */
+
+#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 */
index 76e56301561cd9e70f48cb5f54b95da5b70ffde4..20ac90c721964621068b17cb889a741803b925d8 100644 (file)
@@ -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                         */
 /**************************************************/
index 8a6cf0e377857634d5de06ffa796b3804bf74d19..a285d0d15836a2625fe52985704f69d12000bc6b 100644 (file)
@@ -51,6 +51,7 @@
 #include <net/sock.h>
 
 #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;
     }
index f8ccc19408d0081cd9eef4a8c82ed50e5823239d..3a8d03577e2924bdc9909c00c5a46806df43a101 100644 (file)
@@ -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
index 6da01dd370037db0b9baddba63299e1a4054e47a..cf9a0c40e898b42f03d521bc7be2fcfdb093fc7f 100644 (file)
@@ -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;
index 9497a8a7e489a49f5843f6d3566ee1136d72433d..36338a69b1a447be47203ea333b5e161947a0e23 100644 (file)
@@ -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 (file)
index 0000000..e337349
--- /dev/null
@@ -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 <llcf@volkswagen.de>
+ *
+ */
+
+#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 */
index 7d55ddc45665013173ed093a7a2660f0655181a5..458d6444338f52e3b3ef2f62852830b9f7aa8616 100644 (file)
@@ -52,8 +52,9 @@ RCSID("$Id$");
 
 #include <linux/can/af_can.h>
 
-
 #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
index 3f59e1fb1b2a7ddb9f529a4d809f9f2e64132fc9..67a910278b01b805b01d6b60e686656061a06f01 100644 (file)
@@ -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;
 
index c8cbca203e835d9a32e99605da6051e61da424b0..05f51282eefea94954b6064f8c9ab5bde78dcc42 100644 (file)
@@ -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                         */
 /**************************************************/
index 8411e2237695c7abdf249d1ca40c0e6f4a97e610..c5d196ea0a70fef178373573d8c542fc5fd71c68 100644 (file)
@@ -52,6 +52,7 @@
 #include <net/sock.h>
 
 #include <linux/can/af_can.h>
+#include <linux/can/can_error.h>
 #include <linux/can/raw.h>
 
 #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;
     }
index 2cabf0c3820205b777eab28cce7306b0a93c2a57..6e2cb0bd4de878dccca5d2633e031af1aad9f699 100644 (file)
@@ -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 (file)
index 0000000..fbf0b45
--- /dev/null
@@ -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 <llcf@volkswagen.de>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#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;
+}