Commit c11dcee7 authored by Stephane Grosjean's avatar Stephane Grosjean Committed by Marc Kleine-Budde

can: peak_usb: pcan_usb_decode_error(): upgrade handling of bus state changes

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.comSigned-off-by: default avatarStephane Grosjean <s.grosjean@peak-system.com>
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 1763c547
...@@ -452,145 +452,65 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, ...@@ -452,145 +452,65 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
{ {
struct sk_buff *skb; struct sk_buff *skb;
struct can_frame *cf; 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 */ /* ignore this error until 1st ts received */
if (n == PCAN_USB_ERROR_QOVR) if (n == PCAN_USB_ERROR_QOVR)
if (!mc->pdev->time_ref.tick_count) if (!mc->pdev->time_ref.tick_count)
return 0; 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 */ /* allocate an skb to store the error frame */
skb = alloc_can_err_skb(mc->netdev, &cf); 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: if (n & PCAN_USB_ERROR_RXQOVR) {
cf->can_id |= CAN_ERR_CRTL; /* data overrun interrupt */
cf->data[1] = CAN_ERR_CRTL_ACTIVE; netdev_dbg(mc->netdev, "data overrun interrupt\n");
mc->netdev->stats.rx_over_errors++;
/* sync local copies of rxerr/txerr counters */ mc->netdev->stats.rx_errors++;
mc->pdev->bec.txerr = 0; if (cf) {
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");
cf->can_id |= CAN_ERR_CRTL; cf->can_id |= CAN_ERR_CRTL;
cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; 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; if (n & PCAN_USB_ERROR_TXQFULL)
cf->data[7] = mc->pdev->bec.rxerr; netdev_dbg(mc->netdev, "device Tx queue full)\n");
new_state = mc->pdev->dev.can.state; if (n & PCAN_USB_ERROR_BUS_OFF) {
break; 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) { if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) {
struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb); struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment