can: peak_usb: pcan_usb_decode_error(): upgrade handling of bus state changes
authorStephane Grosjean <s.grosjean@peak-system.com>
Thu, 15 Jul 2021 14:28:41 +0000 (16:28 +0200)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Sun, 25 Jul 2021 09:36:28 +0000 (11:36 +0200)
This patch updates old code by using the can_change_state() function
published since by the socket-can module.

In particular, this new code better manages the change of bus state by
also using the value of the error counters that the driver now
systematically asks for when initializing the channel.

Link: https://lore.kernel.org/r/20210715142842.35793-1-s.grosjean@peak-system.com
Signed-off-by: Stephane Grosjean <s.grosjean@peak-system.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/usb/peak_usb/pcan_usb.c

index fea44e3..e36e60c 100644 (file)
@@ -452,145 +452,65 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
 {
        struct sk_buff *skb;
        struct can_frame *cf;
-       enum can_state new_state;
+       enum can_state new_state = CAN_STATE_ERROR_ACTIVE;
 
        /* ignore this error until 1st ts received */
        if (n == PCAN_USB_ERROR_QOVR)
                if (!mc->pdev->time_ref.tick_count)
                        return 0;
 
-       new_state = mc->pdev->dev.can.state;
-
-       switch (mc->pdev->dev.can.state) {
-       case CAN_STATE_ERROR_ACTIVE:
-               if (n & PCAN_USB_ERROR_BUS_LIGHT) {
-                       new_state = CAN_STATE_ERROR_WARNING;
-                       break;
-               }
-               fallthrough;
-
-       case CAN_STATE_ERROR_WARNING:
-               if (n & PCAN_USB_ERROR_BUS_HEAVY) {
-                       new_state = CAN_STATE_ERROR_PASSIVE;
-                       break;
-               }
-               if (n & PCAN_USB_ERROR_BUS_OFF) {
-                       new_state = CAN_STATE_BUS_OFF;
-                       break;
-               }
-               if (n & ~PCAN_USB_ERROR_BUS) {
-                       /*
-                        * trick to bypass next comparison and process other
-                        * errors
-                        */
-                       new_state = CAN_STATE_MAX;
-                       break;
-               }
-               if ((n & PCAN_USB_ERROR_BUS_LIGHT) == 0) {
-                       /* no error (back to active state) */
-                       new_state = CAN_STATE_ERROR_ACTIVE;
-                       break;
-               }
-               break;
-
-       case CAN_STATE_ERROR_PASSIVE:
-               if (n & PCAN_USB_ERROR_BUS_OFF) {
-                       new_state = CAN_STATE_BUS_OFF;
-                       break;
-               }
-               if (n & PCAN_USB_ERROR_BUS_LIGHT) {
-                       new_state = CAN_STATE_ERROR_WARNING;
-                       break;
-               }
-               if (n & ~PCAN_USB_ERROR_BUS) {
-                       /*
-                        * trick to bypass next comparison and process other
-                        * errors
-                        */
-                       new_state = CAN_STATE_MAX;
-                       break;
-               }
-
-               if ((n & PCAN_USB_ERROR_BUS_HEAVY) == 0) {
-                       /* no error (back to warning state) */
-                       new_state = CAN_STATE_ERROR_WARNING;
-                       break;
-               }
-               break;
-
-       default:
-               /* do nothing waiting for restart */
-               return 0;
-       }
-
-       /* donot post any error if current state didn't change */
-       if (mc->pdev->dev.can.state == new_state)
-               return 0;
-
        /* allocate an skb to store the error frame */
        skb = alloc_can_err_skb(mc->netdev, &cf);
-       if (!skb)
-               return -ENOMEM;
-
-       switch (new_state) {
-       case CAN_STATE_BUS_OFF:
-               cf->can_id |= CAN_ERR_BUSOFF;
-               mc->pdev->dev.can.can_stats.bus_off++;
-               can_bus_off(mc->netdev);
-               break;
-
-       case CAN_STATE_ERROR_PASSIVE:
-               cf->can_id |= CAN_ERR_CRTL;
-               cf->data[1] = (mc->pdev->bec.txerr > mc->pdev->bec.rxerr) ?
-                               CAN_ERR_CRTL_TX_PASSIVE :
-                               CAN_ERR_CRTL_RX_PASSIVE;
-               cf->data[6] = mc->pdev->bec.txerr;
-               cf->data[7] = mc->pdev->bec.rxerr;
-
-               mc->pdev->dev.can.can_stats.error_passive++;
-               break;
-
-       case CAN_STATE_ERROR_WARNING:
-               cf->can_id |= CAN_ERR_CRTL;
-               cf->data[1] = (mc->pdev->bec.txerr > mc->pdev->bec.rxerr) ?
-                               CAN_ERR_CRTL_TX_WARNING :
-                               CAN_ERR_CRTL_RX_WARNING;
-               cf->data[6] = mc->pdev->bec.txerr;
-               cf->data[7] = mc->pdev->bec.rxerr;
-
-               mc->pdev->dev.can.can_stats.error_warning++;
-               break;
 
-       case CAN_STATE_ERROR_ACTIVE:
-               cf->can_id |= CAN_ERR_CRTL;
-               cf->data[1] = CAN_ERR_CRTL_ACTIVE;
-
-               /* sync local copies of rxerr/txerr counters */
-               mc->pdev->bec.txerr = 0;
-               mc->pdev->bec.rxerr = 0;
-               break;
-
-       default:
-               /* CAN_STATE_MAX (trick to handle other errors) */
-               if (n & PCAN_USB_ERROR_TXQFULL)
-                       netdev_dbg(mc->netdev, "device Tx queue full)\n");
-
-               if (n & PCAN_USB_ERROR_RXQOVR) {
-                       netdev_dbg(mc->netdev, "data overrun interrupt\n");
+       if (n & PCAN_USB_ERROR_RXQOVR) {
+               /* data overrun interrupt */
+               netdev_dbg(mc->netdev, "data overrun interrupt\n");
+               mc->netdev->stats.rx_over_errors++;
+               mc->netdev->stats.rx_errors++;
+               if (cf) {
                        cf->can_id |= CAN_ERR_CRTL;
                        cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
-                       mc->netdev->stats.rx_over_errors++;
-                       mc->netdev->stats.rx_errors++;
                }
+       }
 
-               cf->data[6] = mc->pdev->bec.txerr;
-               cf->data[7] = mc->pdev->bec.rxerr;
+       if (n & PCAN_USB_ERROR_TXQFULL)
+               netdev_dbg(mc->netdev, "device Tx queue full)\n");
 
-               new_state = mc->pdev->dev.can.state;
-               break;
+       if (n & PCAN_USB_ERROR_BUS_OFF) {
+               new_state = CAN_STATE_BUS_OFF;
+       } else if (n & PCAN_USB_ERROR_BUS_HEAVY) {
+               new_state = ((mc->pdev->bec.txerr >= 128) ||
+                            (mc->pdev->bec.rxerr >= 128)) ?
+                               CAN_STATE_ERROR_PASSIVE :
+                               CAN_STATE_ERROR_WARNING;
+       } else {
+               new_state = CAN_STATE_ERROR_ACTIVE;
        }
 
-       mc->pdev->dev.can.state = new_state;
+       /* handle change of state */
+       if (new_state != mc->pdev->dev.can.state) {
+               enum can_state tx_state =
+                       (mc->pdev->bec.txerr >= mc->pdev->bec.rxerr) ?
+                               new_state : 0;
+               enum can_state rx_state =
+                       (mc->pdev->bec.txerr <= mc->pdev->bec.rxerr) ?
+                               new_state : 0;
+
+               can_change_state(mc->netdev, cf, tx_state, rx_state);
+
+               if (new_state == CAN_STATE_BUS_OFF) {
+                       can_bus_off(mc->netdev);
+               } else if (cf && (cf->can_id & CAN_ERR_CRTL)) {
+                       /* Supply TX/RX error counters in case of
+                        * controller error.
+                        */
+                       cf->data[6] = mc->pdev->bec.txerr;
+                       cf->data[7] = mc->pdev->bec.rxerr;
+               }
+       }
+
+       if (!skb)
+               return -ENOMEM;
 
        if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) {
                struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb);