can: dev: Consolidate and unify state change handling
authorAndri Yngvason <andri.yngvason@marel.com>
Wed, 3 Dec 2014 17:54:13 +0000 (17:54 +0000)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Sun, 7 Dec 2014 20:22:09 +0000 (21:22 +0100)
The handling of can error states is different between platforms.
This is an attempt to correct that problem.

I've moved this handling into a generic function for changing the
error state. This ensures that error state changes are handled
the same way everywhere (where this function is used).

This new mechanism also adds reverse state transitioning in error
frames, i.e. the user will be notified through the socket interface
when the state goes down.

Signed-off-by: Andri Yngvason <andri.yngvason@marel.com>
Acked-by: Wolfgang Grandegger <wg@grandegger.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/dev.c
include/linux/can/dev.h
include/uapi/linux/can/error.h

index 2cfe5012e4e58c3c4c51b968fba5c9c029e0db44..3ec8f6f25e5f979e16838930295fe4cd66b2841c 100644 (file)
@@ -273,6 +273,84 @@ static int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
        return err;
 }
 
+static void can_update_state_error_stats(struct net_device *dev,
+                                        enum can_state new_state)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (new_state <= priv->state)
+               return;
+
+       switch (new_state) {
+       case CAN_STATE_ERROR_WARNING:
+               priv->can_stats.error_warning++;
+               break;
+       case CAN_STATE_ERROR_PASSIVE:
+               priv->can_stats.error_passive++;
+               break;
+       case CAN_STATE_BUS_OFF:
+       default:
+               break;
+       };
+}
+
+static int can_tx_state_to_frame(struct net_device *dev, enum can_state state)
+{
+       switch (state) {
+       case CAN_STATE_ERROR_ACTIVE:
+               return CAN_ERR_CRTL_ACTIVE;
+       case CAN_STATE_ERROR_WARNING:
+               return CAN_ERR_CRTL_TX_WARNING;
+       case CAN_STATE_ERROR_PASSIVE:
+               return CAN_ERR_CRTL_TX_PASSIVE;
+       default:
+               return 0;
+       }
+}
+
+static int can_rx_state_to_frame(struct net_device *dev, enum can_state state)
+{
+       switch (state) {
+       case CAN_STATE_ERROR_ACTIVE:
+               return CAN_ERR_CRTL_ACTIVE;
+       case CAN_STATE_ERROR_WARNING:
+               return CAN_ERR_CRTL_RX_WARNING;
+       case CAN_STATE_ERROR_PASSIVE:
+               return CAN_ERR_CRTL_RX_PASSIVE;
+       default:
+               return 0;
+       }
+}
+
+void can_change_state(struct net_device *dev, struct can_frame *cf,
+                     enum can_state tx_state, enum can_state rx_state)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       enum can_state new_state = max(tx_state, rx_state);
+
+       if (unlikely(new_state == priv->state)) {
+               netdev_warn(dev, "%s: oops, state did not change", __func__);
+               return;
+       }
+
+       netdev_dbg(dev, "New error state: %d\n", new_state);
+
+       can_update_state_error_stats(dev, new_state);
+       priv->state = new_state;
+
+       if (unlikely(new_state == CAN_STATE_BUS_OFF)) {
+               cf->can_id |= CAN_ERR_BUSOFF;
+               return;
+       }
+
+       cf->can_id |= CAN_ERR_CRTL;
+       cf->data[1] |= tx_state >= rx_state ?
+                      can_tx_state_to_frame(dev, tx_state) : 0;
+       cf->data[1] |= tx_state <= rx_state ?
+                      can_rx_state_to_frame(dev, rx_state) : 0;
+}
+EXPORT_SYMBOL_GPL(can_change_state);
+
 /*
  * Local echo of CAN messages
  *
index b37ea95bc348f15b16a9c0d095588379b12edbee..c05ff0f9f9a55afec230435183dcce0d93bd5725 100644 (file)
@@ -127,6 +127,9 @@ void unregister_candev(struct net_device *dev);
 int can_restart_now(struct net_device *dev);
 void can_bus_off(struct net_device *dev);
 
+void can_change_state(struct net_device *dev, struct can_frame *cf,
+                     enum can_state tx_state, enum can_state rx_state);
+
 void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
                      unsigned int idx);
 unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx);
index c247446ab25a4e564a068ae97e154cf7972f0a1d..1c508be9687fc593dc4a5baee0e0e443ff4e8e71 100644 (file)
@@ -71,6 +71,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 */