I found some time to work on this issue.

So far we only report CAN error state changes to the worse:
"error-active -> error-warning -> error-passive -> bus-off", but not
"bus-off -> error-active" or "error-passive -> error-warning ->
error-active". There have been request from other users to correct
this behavior.

This patch reports any state change reported by the SJA1000 and
MSCAN-MPC5200 controller. It introduces "CAN_ERR_CRTL_ACTIVE" to
signal state changes to error active. Furthermore, the SJA1000
triggers now the bus-off recovery procedure when restarted. So far,
the controller was *stopped* and then restarted. I did two tests to
analyze the state changes behavior:

1. First I forced the controller into the error-passive by sending
   a message (with cansend) without cable connected (*). Then I
   reconnected the cable (**) and continued to send messages (with
   cangen) (***) until the error active state is reached:

   On SJA1000:

   # candump any,0:0,#FFFFFFFF
   (*)
   can0  20000004  [8] 00 08 00 00 00 00 60 00   ERROR-WARNING
   can0  20000004  [8] 00 20 00 00 00 00 80 00   ERROR-PASSIVE
   (**)
   can0  123  [4] DE AD BE EF
   (***)
   can0  42A  [1] 00
   ...
   can0  42A  [1] 07
   can0  20000004  [8] 00 08 00 00 00 00 7F 00   ERROR-WARNING
   can0  42A  [1] 08
   ...
   can0  42A  [1] 27
   can0  20000004  [8] 00 40 00 00 00 00 5F 00   ERROR-ACTIVE
                                          \-- tx-err !!!
   On MSCAN:

   # candump any,0:0,#FFFFFFFF
   (*)
   can2  20000004  [8] 00 08 00 00 00 00 00 00   ERROR-WARNING
   can2  20000004  [8] 00 20 00 00 00 00 00 00   ERROR-PASSIVE
   (**)
   can2  123  [4] DE AD BE EF
   can2  20000004  [8] 00 08 00 00 00 00 00 00   ERROR-WARNING
   (***)
   can2  42A  [1] 00
   can2  42A  [1] 01
   ...
   can2  42A  [1] 1E
   can2  42A  [1] 1F
   can2  20000004  [8] 00 40 00 00 00 00 00 00   ERROR-ACTIVE

   As you can see, the state change to error-active is signaled
   differently. The MSCAN seems not to respect the specs :-(.
   When time permits, I will also try the MSCAN of the MPC5121.

2. I forced the controller into bus-off by short-circuiting
   the low and hi lines and sending a message (*), Then I
   restarted the controller manually with
   "ip link set can0 type can restart" (**):

   On SJA1000:

   # ./candump -t d any,0:0,#FFFFFFFF
   (*)
   can0  20000004  [8] 00 08 00 00 00 00 88 00   ERROR-WARNING
   can0  20000004  [8] 00 20 00 00 00 00 88 00   ERROR-PASSIVE
   can0  20000044  [8] 00 00 00 00 00 00 7F 00   BUS-OFF
   (**)
   can0  20000100  [8] 00 00 00 00 00 00 00 00   RESTARTED
   can0  20000004  [8] 00 40 00 00 00 00 00 00   ERROR-ACTIVE
                                          \-- tx-err !!!

   On MSCAN:

   # ./candump -t d any,0:0,#FFFFFFFF
   (*)
   can2  20000004  [8] 00 08 00 00 00 00 00 00   ERROR-WARNING
   can2  20000040  [8] 00 00 00 00 00 00 00 00   BUS-OFF
   (**)
   can2  20000100  [8] 00 00 00 00 00 00 00 00   RESTARTED
   can2  20000004  [8] 00 40 00 00 00 00 00 00   ERROR-ACTIVE

   The MSCAN seems not to signal the ERROR-PASSIVE state before
   going bus-off.

Would be interesting to see, how other CAN controllers behave.
Volunteers are welcome.

What do you think? Would that patch/approach fit your needs?

Wolfgang.

---
 drivers/net/can/mscan/mscan.c     |   17 ++++++++++----
 drivers/net/can/sja1000/sja1000.c |   45 +++++++++++++++++++++++++++-----------
 include/linux/can/error.h         |    1 
 3 files changed, 46 insertions(+), 17 deletions(-)

Index: net-next-2.6/drivers/net/can/mscan/mscan.c
===================================================================
--- net-next-2.6.orig/drivers/net/can/mscan/mscan.c
+++ net-next-2.6/drivers/net/can/mscan/mscan.c
@@ -363,20 +363,29 @@ static void mscan_get_err_frame(struct n
        /* State changed */
        if (old_state != priv->can.state) {
                switch (priv->can.state) {
+               case CAN_STATE_ERROR_ACTIVE:
+                       frame->can_id |= CAN_ERR_CRTL;
+                       frame->data[1] = CAN_ERR_CRTL_ACTIVE;
+                       break;
                case CAN_STATE_ERROR_WARNING:
                        frame->can_id |= CAN_ERR_CRTL;
                        priv->can.can_stats.error_warning++;
-                       if ((priv->shadow_statflg & MSCAN_RSTAT_MSK) <
+                       if ((priv->shadow_statflg & MSCAN_RSTAT_MSK) !=
                            (canrflg & MSCAN_RSTAT_MSK))
                                frame->data[1] |= CAN_ERR_CRTL_RX_WARNING;
-                       if ((priv->shadow_statflg & MSCAN_TSTAT_MSK) <
+                       if ((priv->shadow_statflg & MSCAN_TSTAT_MSK) !=
                            (canrflg & MSCAN_TSTAT_MSK))
                                frame->data[1] |= CAN_ERR_CRTL_TX_WARNING;
                        break;
                case CAN_STATE_ERROR_PASSIVE:
                        frame->can_id |= CAN_ERR_CRTL;
                        priv->can.can_stats.error_passive++;
-                       frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+                       if ((priv->shadow_statflg & MSCAN_RSTAT_MSK) !=
+                           (canrflg & MSCAN_RSTAT_MSK))
+                               frame->data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+                       if ((priv->shadow_statflg & MSCAN_TSTAT_MSK) !=
+                           (canrflg & MSCAN_TSTAT_MSK))
+                               frame->data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
                        break;
                case CAN_STATE_BUS_OFF:
                        frame->can_id |= CAN_ERR_BUSOFF;
@@ -393,8 +402,6 @@ static void mscan_get_err_frame(struct n
                        }
                        can_bus_off(dev);
                        break;
-               default:
-                       break;
                }
        }
        priv->shadow_statflg = canrflg & MSCAN_STAT_MSK;
Index: net-next-2.6/drivers/net/can/sja1000/sja1000.c
===================================================================
--- net-next-2.6.orig/drivers/net/can/sja1000/sja1000.c
+++ net-next-2.6/drivers/net/can/sja1000/sja1000.c
@@ -165,6 +165,12 @@ static void sja1000_start(struct net_dev
 {
        struct sja1000_priv *priv = netdev_priv(dev);
 
+       if (priv->can.state == CAN_STATE_BUS_OFF) {
+               /* trigger bus-off recovery? */
+               priv->write_reg(priv, REG_MOD, 0x00);
+               return;
+       }
+
        /* leave reset mode */
        if (priv->can.state != CAN_STATE_STOPPED)
                set_reset_mode(dev);
@@ -390,7 +396,8 @@ static int sja1000_err(struct net_device
 
        if (isrc & IRQ_EI) {
                /* error warning interrupt */
-               dev_dbg(dev->dev.parent, "error warning interrupt\n");
+               dev_dbg(dev->dev.parent, "error warning interrupt (sr=%02x)\n",
+                       status);
 
                if (status & SR_BS) {
                        state = CAN_STATE_BUS_OFF;
@@ -398,8 +405,9 @@ static int sja1000_err(struct net_device
                        can_bus_off(dev);
                } else if (status & SR_ES) {
                        state = CAN_STATE_ERROR_WARNING;
-               } else
+               } else {
                        state = CAN_STATE_ERROR_ACTIVE;
+               }
        }
        if (isrc & IRQ_BEI) {
                /* bus error interrupt */
@@ -431,11 +439,16 @@ static int sja1000_err(struct net_device
        }
        if (isrc & IRQ_EPI) {
                /* error passive interrupt */
-               dev_dbg(dev->dev.parent, "error passive interrupt\n");
-               if (status & SR_ES)
-                       state = CAN_STATE_ERROR_PASSIVE;
-               else
+               dev_dbg(dev->dev.parent, "error passive interrupt (sr=%02x)\n",
+                       status);
+               if (status & SR_ES) {
+                       if (priv->can.state == CAN_STATE_ERROR_WARNING)
+                               state = CAN_STATE_ERROR_PASSIVE;
+                       else
+                               state = CAN_STATE_ERROR_WARNING;
+               } else {
                        state = CAN_STATE_ERROR_ACTIVE;
+               }
        }
        if (isrc & IRQ_ALI) {
                /* arbitration lost interrupt */
@@ -447,28 +460,36 @@ static int sja1000_err(struct net_device
                cf->data[0] = alc & 0x1f;
        }
 
-       if (state != priv->can.state && (state == CAN_STATE_ERROR_WARNING ||
-                                        state == CAN_STATE_ERROR_PASSIVE)) {
+       if (state != priv->can.state) {
                uint8_t rxerr = priv->read_reg(priv, REG_RXERR);
                uint8_t txerr = priv->read_reg(priv, REG_TXERR);
                cf->can_id |= CAN_ERR_CRTL;
-               if (state == CAN_STATE_ERROR_WARNING) {
+
+               switch (state) {
+               case CAN_STATE_ERROR_ACTIVE:
+                       cf->data[1] = CAN_ERR_CRTL_ACTIVE;
+                       break;
+               case CAN_STATE_ERROR_WARNING:
                        priv->can.can_stats.error_warning++;
                        cf->data[1] = (txerr > rxerr) ?
                                CAN_ERR_CRTL_TX_WARNING :
                                CAN_ERR_CRTL_RX_WARNING;
-               } else {
+                       break;
+               case CAN_STATE_ERROR_PASSIVE:
                        priv->can.can_stats.error_passive++;
                        cf->data[1] = (txerr > rxerr) ?
                                CAN_ERR_CRTL_TX_PASSIVE :
                                CAN_ERR_CRTL_RX_PASSIVE;
+                       break;
+               default:
+                       break;
                }
+
                cf->data[6] = txerr;
                cf->data[7] = rxerr;
+               priv->can.state = state;
        }
 
-       priv->can.state = state;
-
        netif_rx(skb);
 
        stats->rx_packets++;
Index: net-next-2.6/include/linux/can/error.h
===================================================================
--- net-next-2.6.orig/include/linux/can/error.h
+++ net-next-2.6/include/linux/can/error.h
@@ -41,6 +41,7 @@
 #define CAN_ERR_CRTL_TX_PASSIVE  0x20 /* reached error passive status TX */
                                      /* (at least one error counter exceeds */
                                      /* the protocol-defined level of 127)  */
+#define CAN_ERR_CRTL_ACTIVE      0x40 /* recovered to error active state */
 
 /* error in CAN protocol (type) / data[2] */
 #define CAN_ERR_PROT_UNSPEC      0x00 /* unspecified */
_______________________________________________
Socketcan-core mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

Reply via email to