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 */
}
}
+out:
return matches;
}
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 */
}
}
+ if (err) /* error frames */
+ return &p->rx_err;
+
if (inv) /* inverse can_id/can_mask filter and RTR */
return &p->rx_inv;
} can_addr;
};
+typedef canid_t can_err_mask_t;
+
struct can_filter {
canid_t can_id;
canid_t can_mask;
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;
/* 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) */
--- /dev/null
+/*
+ * 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 */
#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);
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;
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 */
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) */
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);
}
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 */
/**************************************************/
#include <net/sock.h>
#include "af_can.h"
+#include "can_error.h"
#include "version.h"
#include "raw.h"
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,
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);
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);
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;
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;
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) {
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);
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;
}
#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
} can_addr;
};
+typedef canid_t can_err_mask_t;
+
struct can_filter {
canid_t can_id;
canid_t can_mask;
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;
/* 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) */
--- /dev/null
+/*
+ * 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 */
#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
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 */
}
}
+out:
return matches;
}
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 */
}
}
+ if (err) /* error frames */
+ return &p->rx_err;
+
if (inv) /* inverse can_id/can_mask filter and RTR */
return &p->rx_inv;
#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);
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;
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 */
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) */
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);
}
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 */
/**************************************************/
#include <net/sock.h>
#include <linux/can/af_can.h>
+#include <linux/can/can_error.h>
#include <linux/can/raw.h>
#include "version.h"
int ifindex;
int count;
struct can_filter *filter;
+ can_err_mask_t err_mask;
};
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,13)
#endif
+#define MASK_ALL 0
static __init int raw_init(void)
{
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);
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);
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;
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;
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) {
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);
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;
}
PROGRAMS_GPL = tst-raw \
tst-raw-filter \
+ tst-err \
tst-raw-sendto \
tst-bcm-cycle \
tst-bcm-tx_read \
--- /dev/null
+/*
+ * $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;
+}