Re: [PATCH 2/2] can/peak_usb/pcan_usb: add support of rxerr/txerr counters

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 10/8/19 10:35 AM, Stephane Grosjean wrote:
> This patch adds the support of the rx/tx errors CAN counters to the
> driver of the PCAN-USB PC-CAN interface from PEAK-System GmbH.
> 
> The PCAN-USB is internally equipped with a SJA1000 -like CAN controller,
> therefore it is capable of giving back the values of the rx/tx errors
> counters, to provide more details and statistics to the linux-can layer.
> Moreover, getting these values allows the driver to better tune
> CAN_ERR_CRTL_TX_xxx and CAN_ERR_CRTL_RX_xxx bits in case of
> the interface enters the CAN_STATE_ERROR_xxx state.
> 
> Signed-off-by: Stephane Grosjean <s.grosjean@xxxxxxxxxxxxxxx>
> ---
>  drivers/net/can/usb/peak_usb/pcan_usb.c | 175 ++++++++++++++++++++++--
>  1 file changed, 160 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c
> index d2539c95adb6..b0b722cb327c 100644
> --- a/drivers/net/can/usb/peak_usb/pcan_usb.c
> +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c
> @@ -66,6 +66,10 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter");
>  #define PCAN_USB_ERROR_QOVR		0x40
>  #define PCAN_USB_ERROR_TXQFULL		0x80
>  
> +#define PCAN_USB_ERROR_BUS		(PCAN_USB_ERROR_BUS_LIGHT | \
> +					 PCAN_USB_ERROR_BUS_HEAVY | \
> +					 PCAN_USB_ERROR_BUS_OFF)
> +
>  /* SJA1000 modes */
>  #define SJA1000_MODE_NORMAL		0x00
>  #define SJA1000_MODE_INIT		0x01
> @@ -85,11 +89,31 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB adapter");
>  #define PCAN_USB_REC_TS			4
>  #define PCAN_USB_REC_BUSEVT		5
>  
> +/* CAN bus errors notifications */
> +#define PCAN_USB_ERR_ECC		0x01
> +#define PCAN_USB_ERR_RXERR		0x02
> +#define PCAN_USB_ERR_TXERR		0x04
> +#define PCAN_USB_ERR_RXERR_CNT		0x08
> +#define PCAN_USB_ERR_TXERR_CNT		0x10
> +
> +/* SJA1000 ECC register */

please add the common PCAN_ prefix (or PCAN_SJA1000_, ...)

> +#define ECC_SEG				0x1f
> +#define ECC_DIR				0x20
> +#define ECC_ERR				6
> +#define ECC_BIT				0x00
> +#define ECC_FORM			0x40
> +#define ECC_STUFF			0x80
> +#define ECC_MASK			0xc0
> +
> +/* SJA1000 Bus Error Interrupt */
> +#define IRQ_BEI				0x80

same here

> +
>  /* private to PCAN-USB adapter */
>  struct pcan_usb {
>  	struct peak_usb_device dev;
>  	struct peak_time_ref time_ref;
>  	struct timer_list restart_timer;
> +	struct can_berr_counter bec;
>  };
>  
>  /* incoming message context for decoding */
> @@ -193,6 +217,16 @@ static int pcan_usb_set_silent(struct peak_usb_device *dev, u8 onoff)
>  	return pcan_usb_send_cmd(dev, 3, 3, args);
>  }
>  
> +/* send the cmd to be notified from bus errors */
> +static int pcan_usb_set_err_frame(struct peak_usb_device *dev, u8 err_mask)
> +{
> +	u8 args[PCAN_USB_CMD_ARGS_LEN] = {
> +		[0] = err_mask,
> +	};
> +
> +	return pcan_usb_send_cmd(dev, 11, 2, args);

What does 11 and 2 mean? But in the other calls it's not documented either.

> +}
> +
>  static int pcan_usb_set_ext_vcc(struct peak_usb_device *dev, u8 onoff)
>  {
>  	u8 args[PCAN_USB_CMD_ARGS_LEN] = {
> @@ -426,7 +460,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
>  			new_state = CAN_STATE_BUS_OFF;
>  			break;
>  		}
> -		if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) {
> +		if (n & ~PCAN_USB_ERROR_BUS) {
>  			/*
>  			 * trick to bypass next comparison and process other
>  			 * errors
> @@ -450,7 +484,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
>  			new_state = CAN_STATE_ERROR_WARNING;
>  			break;
>  		}
> -		if (n & (PCAN_USB_ERROR_RXQOVR | PCAN_USB_ERROR_QOVR)) {
> +		if (n & ~PCAN_USB_ERROR_BUS) {
>  			/*
>  			 * trick to bypass next comparison and process other
>  			 * errors
> @@ -489,15 +523,23 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
>  
>  	case CAN_STATE_ERROR_PASSIVE:
>  		cf->can_id |= CAN_ERR_CRTL;
> -		cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE |
> -			       CAN_ERR_CRTL_RX_PASSIVE;
> +		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] |= CAN_ERR_CRTL_TX_WARNING |
> -			       CAN_ERR_CRTL_RX_WARNING;
> +		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;
>  
> @@ -508,10 +550,19 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
>  
>  	default:
>  		/* CAN_STATE_MAX (trick to handle other errors) */
> -		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++;
> +		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;
> @@ -533,6 +584,74 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n,
>  	return 0;
>  }
>  
> +static int pcan_usb_handle_bus_evt(struct pcan_usb_msg_context *mc, u8 ir,
> +				   u8 status_len)
> +{
> +	struct pcan_usb *pdev = mc->pdev;
> +
> +	/* process bus error interrupt */
> +	if (ir & IRQ_BEI) {
> +		struct net_device_stats *stats = &mc->netdev->stats;
> +		struct sk_buff *skb;
> +		struct can_frame *cf;
> +		u8 ecc = mc->ptr[0];
> +
> +		/* save rx/tx error counters from data bytes */
> +		pdev->bec.rxerr = mc->ptr[1];
> +		pdev->bec.txerr = mc->ptr[2];
> +
> +		/* allocate an skb to store the error frame */
> +		skb = alloc_can_err_skb(mc->netdev, &cf);
> +		if (!skb)
> +			return -ENOMEM;
> +
> +		cf->data[6] = pdev->bec.txerr;
> +		cf->data[7] = pdev->bec.rxerr;
> +
> +		/* The PCAN-USB is equipped with a SJA1000, thus copy from
> +		 * sja1000.c how to handle the BEI:
> +		 */
> +		pdev->dev.can.can_stats.bus_error++;
> +		stats->rx_errors++;
> +
> +		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +
> +		/* set error type according to 1st data byte (ECC) */
> +		switch (ecc & ECC_MASK) {
> +		case ECC_BIT:
> +			cf->data[2] |= CAN_ERR_PROT_BIT;
> +			break;
> +		case ECC_FORM:
> +			cf->data[2] |= CAN_ERR_PROT_FORM;
> +			break;
> +		case ECC_STUFF:
> +			cf->data[2] |= CAN_ERR_PROT_STUFF;
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		/* set error location */
> +		cf->data[3] = ecc & ECC_SEG;
> +
> +		/* Error occurred during transmission? */
> +		if ((ecc & ECC_DIR) == 0)
> +			cf->data[2] |= CAN_ERR_PROT_TX;
> +		if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) {
> +			struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb);
> +
> +			peak_usb_get_ts_time(&mc->pdev->time_ref, mc->ts16,
> +					     &hwts->hwtstamp);
> +		}
> +
> +		stats->rx_packets++;
> +		stats->rx_bytes += cf->can_dlc;
> +		netif_rx(skb);
> +	}
> +
> +	return 0;
> +}
> +
>  /*
>   * decode non-data usb message
>   */
> @@ -587,9 +706,9 @@ static int pcan_usb_decode_status(struct pcan_usb_msg_context *mc,
>  		break;
>  
>  	case PCAN_USB_REC_BUSEVT:
> -		/* error frame/bus event */
> -		if (n & PCAN_USB_ERROR_TXQFULL)
> -			netdev_dbg(mc->netdev, "device Tx queue full)\n");
> +		err = pcan_usb_handle_bus_evt(mc, n, status_len);
> +		if (err)
> +			return err;
>  		break;
>  	default:
>  		netdev_err(mc->netdev, "unexpected function %u\n", f);
> @@ -773,20 +892,45 @@ static int pcan_usb_encode_msg(struct peak_usb_device *dev, struct sk_buff *skb,
>  	return 0;
>  }
>  
> +/* socket callback used to copy berr counters values receieved through USB */
> +static int pcan_usb_get_berr_counter(const struct net_device *netdev,
> +				     struct can_berr_counter *bec)
> +{
> +	struct peak_usb_device *dev = netdev_priv(netdev);
> +	struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
> +
> +	*bec = pdev->bec;
> +
> +	/* must return 0 */
> +	return 0;
> +}
> +
>  /*
>   * start interface
>   */
>  static int pcan_usb_start(struct peak_usb_device *dev)
>  {
>  	struct pcan_usb *pdev = container_of(dev, struct pcan_usb, dev);
> +	int err;
>  
>  	/* number of bits used in timestamps read from adapter struct */
>  	peak_usb_init_time_ref(&pdev->time_ref, &pcan_usb);
>  
> +	pdev->bec.rxerr = 0;
> +	pdev->bec.txerr = 0;
> +
> +	/* be notified on any error counter change */

Does the device offer the possibility to read the error counters? If so
please do it in pcan_usb_get_berr_counter().

> +	err = pcan_usb_set_err_frame(dev, PCAN_USB_ERR_ECC |
> +				     PCAN_USB_ERR_RXERR | PCAN_USB_ERR_TXERR |
> +				     PCAN_USB_ERR_RXERR_CNT |
> +				     PCAN_USB_ERR_TXERR_CNT);

Better only enable bus errors, if the user has requested them. See:


https://elixir.bootlin.com/linux/latest/source/drivers/net/can/sja1000/sja1000.c#L152

> +	if (err)
> +		netdev_warn(dev->netdev,
> +			    "CAN bus error counters not available (err %u)\n",
> +			    err);
> +
>  	/* if revision greater than 3, can put silent mode on/off */
>  	if (dev->device_rev > 3) {
> -		int err;
> -
>  		err = pcan_usb_set_silent(dev,
>  				dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY);
>  		if (err)
> @@ -906,4 +1050,5 @@ const struct peak_usb_adapter pcan_usb = {
>  	.dev_encode_msg = pcan_usb_encode_msg,
>  	.dev_start = pcan_usb_start,
>  	.dev_restart_async = pcan_usb_restart_async,
> +	.do_get_berr_counter = pcan_usb_get_berr_counter,
>  };
> 

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |

Attachment: signature.asc
Description: OpenPGP digital signature


[Index of Archives]     [Automotive Discussions]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]     [CAN Bus]

  Powered by Linux