|
@@ -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
|
|
|
*
|