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; /* allocate an skb to store the error frame */
skb = alloc_can_err_skb(mc->netdev, &cf);
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_RXQOVR) {
if (n & PCAN_USB_ERROR_BUS_HEAVY) { /* data overrun interrupt */
new_state = CAN_STATE_ERROR_PASSIVE; netdev_dbg(mc->netdev, "data overrun interrupt\n");
break; mc->netdev->stats.rx_over_errors++;
} mc->netdev->stats.rx_errors++;
if (n & PCAN_USB_ERROR_BUS_OFF) { if (cf) {
new_state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_CRTL;
break; cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
}
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_TXQFULL)
netdev_dbg(mc->netdev, "device Tx queue full)\n");
if (n & PCAN_USB_ERROR_BUS_OFF) { if (n & PCAN_USB_ERROR_BUS_OFF) {
new_state = CAN_STATE_BUS_OFF; new_state = CAN_STATE_BUS_OFF;
break; } else if (n & PCAN_USB_ERROR_BUS_HEAVY) {
} new_state = ((mc->pdev->bec.txerr >= 128) ||
if (n & PCAN_USB_ERROR_BUS_LIGHT) { (mc->pdev->bec.rxerr >= 128)) ?
new_state = CAN_STATE_ERROR_WARNING; CAN_STATE_ERROR_PASSIVE :
break; CAN_STATE_ERROR_WARNING;
} } else {
if (n & ~PCAN_USB_ERROR_BUS) { new_state = CAN_STATE_ERROR_ACTIVE;
/*
* 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 */ /* handle change of state */
if (mc->pdev->dev.can.state == new_state) if (new_state != mc->pdev->dev.can.state) {
return 0; 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;
/* allocate an skb to store the error frame */ can_change_state(mc->netdev, cf, tx_state, rx_state);
skb = alloc_can_err_skb(mc->netdev, &cf);
if (!skb)
return -ENOMEM;
switch (new_state) { if (new_state == CAN_STATE_BUS_OFF) {
case CAN_STATE_BUS_OFF:
cf->can_id |= CAN_ERR_BUSOFF;
mc->pdev->dev.can.can_stats.bus_off++;
can_bus_off(mc->netdev); can_bus_off(mc->netdev);
break; } else if (cf && (cf->can_id & CAN_ERR_CRTL)) {
/* Supply TX/RX error counters in case of
case CAN_STATE_ERROR_PASSIVE: * controller error.
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[6] = mc->pdev->bec.txerr;
cf->data[7] = mc->pdev->bec.rxerr; 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");
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;
new_state = mc->pdev->dev.can.state;
break;
} }
mc->pdev->dev.can.state = new_state; 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