linux-can.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Marc Kleine-Budde <mkl@pengutronix.de>
To: Alexander Stein <alexander.stein@systec-electronic.com>,
	Wolfgang Grandegger <wg@grandegger.com>
Cc: linux-can@vger.kernel.org
Subject: Re: [PATCH 5/5] can: slcan: Add CAN status flag support
Date: Thu, 17 Apr 2014 21:22:03 +0200	[thread overview]
Message-ID: <535029DB.5010105@pengutronix.de> (raw)
In-Reply-To: <1397573468-7619-6-git-send-email-alexander.stein@systec-electronic.com>

[-- Attachment #1: Type: text/plain, Size: 10119 bytes --]

On 04/15/2014 04:51 PM, Alexander Stein wrote:
> CAN status flags have to be polled from the serial device. So send the
> request every 10ms by default.
> 
> Signed-off-by: Alexander Stein <alexander.stein@systec-electronic.com>
> ---
>  drivers/net/can/Kconfig |   4 +-
>  drivers/net/can/slcan.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 188 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index 267aec2..9aa38943 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -113,8 +113,8 @@ config CAN_SLCAN
>  	  via serial lines or via USB-to-serial adapters using the LAWICEL
>  	  ASCII protocol. The driver implements the tty linediscipline N_SLCAN.
>  
> -	  Sending, receiving of CAN frames as well as bitrate setting is
> -	  implemented, this driver should work with the
> +	  Sending, receiving of CAN frames as well as bitrate setting and
> +	  error handling is implemented, this driver should work with the
>  	  (serial/USB) CAN hardware from:
>  	  www.canusb.com / www.can232.com / www.mictronics.de / www.canhack.de
>  
> diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
> index 8d98f50..35e5392 100644
> --- a/drivers/net/can/slcan.c
> +++ b/drivers/net/can/slcan.c
> @@ -72,6 +72,15 @@ static int maxdev = 10;		/* MAX number of SLCAN channels;
>  module_param(maxdev, int, 0);
>  MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces");
>  
> +/** \brief Status poll interval (in ms) in which the CAN status shall be polled
> + *
> + * Interval (in ms) in which the CAN status shall be polled.
> + * Can be overridden with insmod slcan.ko status_interval_ms=nnn
> + */
> +static int status_interval_ms = 10;
> +module_param(status_interval_ms, uint, 0);
> +MODULE_PARM_DESC(status_interval_ms, "Status poll interval [ms]");
> +
>  /* maximum rx buffer len: extended CAN frame with timestamp */
>  #define SLC_MTU (sizeof("T1111222281122334455667788EA5F\r")+1)
>  
> @@ -98,6 +107,20 @@ struct slcan {
>  	unsigned long		flags;		/* Flag values/ mode etc     */
>  #define SLF_INUSE		0		/* Channel in use            */
>  #define SLF_ERROR		1               /* Parity, etc. error        */
> +	unsigned char		can_flags;	/* CAN status flags          */
> +/* #define SLC_CAN_FLAG_RFIFO_FULL BIT(0) */
> +/* #define SLC_CAN_FLAG_TFIFO_FULL BIT(1) */
> +#define SLC_CAN_FLAG_ERR_WARN   BIT(2)
> +#define SLC_CAN_FLAG_OVERRUN    BIT(3)
> +/* 4 is unused */
> +#define SLC_CAN_FLAG_ERR_PASV   BIT(5)
> +#define SLC_CAN_FLAG_ARB_LOST   BIT(6)
> +#define SLC_CAN_FLAG_BUS_ERR    BIT(7)
> +#define SLC_CAN_FLAG_VALID (SLC_CAN_FLAG_ERR_WARN | SLC_CAN_FLAG_OVERRUN \
> +		| SLC_CAN_FLAG_ERR_PASV | SLC_CAN_FLAG_ARB_LOST \
> +		| SLC_CAN_FLAG_BUS_ERR)
> +
> +	struct delayed_work work;
>  };
>  
>  static struct net_device **slcan_devs;
> @@ -105,6 +128,18 @@ static DEFINE_MUTEX(slcan_mutex);
>  
>  #define CLOSE_COMMAND "C\r"
>  #define OPEN_COMMAND "O\r"
> +#define FLAGS_COMMAND "F\r"
> +
> +static void slcan_queue_work(struct slcan *sl)
> +{
> +	unsigned long delay;
> +
> +	delay = msecs_to_jiffies(status_interval_ms);
> +	if (delay >= HZ)
> +		delay = round_jiffies_relative(delay);
> +
> +	queue_delayed_work(system_freezable_wq, &sl->work, delay);
> +}
>  
>  static int slc_open_device(struct slcan *sl) __must_hold(&sl->lock)
>  {
> @@ -119,6 +154,7 @@ static int slc_open_device(struct slcan *sl) __must_hold(&sl->lock)
>  		}
>  	}
>  	sl->can.state = CAN_STATE_ERROR_ACTIVE;
> +	slcan_queue_work(sl);
>  	return 0;
>  }
>  
> @@ -126,6 +162,11 @@ static int slc_close_device(struct slcan *sl) __must_hold(&sl->lock)
>  {
>  	int written;
>  
> +	/* Release the lock while waiting for the delayed work to finish */
> +	spin_unlock_bh(&sl->lock);
> +	cancel_delayed_work_sync(&sl->work);
> +	spin_lock_bh(&sl->lock);
> +
>  	if (sl->tty) {
>  		written = sl->tty->ops->write(sl->tty, CLOSE_COMMAND,
>  					      strlen(CLOSE_COMMAND));
> @@ -137,6 +178,22 @@ static int slc_close_device(struct slcan *sl) __must_hold(&sl->lock)
>  	return 0;
>  }
>  
> +static void slcan_status_work(struct work_struct *work)
> +{
> +	struct slcan *sl = container_of(work, struct slcan, work.work);
> +	int written;
> +
> +	spin_lock_bh(&sl->lock);
> +	if (sl->tty) {
> +		written = sl->tty->ops->write(sl->tty, FLAGS_COMMAND,
> +					      strlen(FLAGS_COMMAND));
> +		if (written != strlen(FLAGS_COMMAND))
> +			netdev_warn(sl->dev, "Flags command could not be sent!\n");
> +	}
> +	spin_unlock_bh(&sl->lock);
> +	slcan_queue_work(sl);
> +}
> +
>   /************************************************************************
>    *			SLCAN ENCAPSULATION FORMAT			 *
>    ************************************************************************/
> @@ -176,6 +233,94 @@ static int slc_close_device(struct slcan *sl) __must_hold(&sl->lock)
>    *			STANDARD SLCAN DECAPSULATION			 *
>    ************************************************************************/
>  
> +/* Analyse the error flags and send an error frame if approriate */
> +static void slc_handle_flags(struct slcan *sl)
> +{
> +	struct sk_buff *skb;
> +	struct can_frame *cf;
> +	struct net_device_stats *stats = &sl->dev->stats;
> +	enum can_state state = sl->dev->state;
> +	u8 new_flags;
> +
> +	if (hex2bin(&new_flags, sl->rbuff + SLC_CMD_LEN, 1))
> +		return;
> +
> +	new_flags &= SLC_CAN_FLAG_VALID;
> +
> +	if (!(new_flags ^ sl->can_flags))
> +		return;
> +
> +	skb = alloc_can_err_skb(sl->dev, &cf);
> +	if (!skb)
> +		return;

Can you please move the stats handling, so that they are updated, even
if the allocation of the skb fails.

> +
> +	state = CAN_STATE_ERROR_ACTIVE;
> +	if (new_flags & SLC_CAN_FLAG_ARB_LOST) {
> +		sl->can.can_stats.arbitration_lost++;
> +		stats->tx_errors++;
> +		cf->can_id |= CAN_ERR_LOSTARB;
> +	}
> +	if (new_flags & SLC_CAN_FLAG_OVERRUN) {
> +		cf->can_id |= CAN_ERR_CRTL;
> +		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +		stats->rx_over_errors++;
> +		stats->rx_errors++;
> +	}
> +	if (new_flags & SLC_CAN_FLAG_ERR_WARN)
> +		state = CAN_STATE_ERROR_WARNING;
> +	if (new_flags & SLC_CAN_FLAG_ERR_PASV)
> +		state = CAN_STATE_ERROR_PASSIVE;
> +	if (new_flags & SLC_CAN_FLAG_BUS_ERR)
> +		state = CAN_STATE_BUS_OFF;
> +
> +	if (state != sl->can.state) {
> +		stats->rx_errors++;
> +
> +		switch (state) {
> +		case CAN_STATE_ERROR_ACTIVE:
> +			cf->can_id |= CAN_ERR_PROT;
> +			cf->data[2] = CAN_ERR_PROT_ACTIVE;
> +			break;
> +		case CAN_STATE_ERROR_WARNING:
> +			cf->can_id |= CAN_ERR_CRTL;
> +			cf->data[1] = CAN_ERR_CRTL_RX_WARNING |
> +					CAN_ERR_CRTL_TX_WARNING;
> +			sl->can.can_stats.error_warning++;
> +			break;
> +		case CAN_STATE_ERROR_PASSIVE:
> +			cf->can_id |= CAN_ERR_CRTL;
> +			cf->data[1] = CAN_ERR_CRTL_RX_PASSIVE |
> +					CAN_ERR_CRTL_TX_PASSIVE;
> +			sl->can.can_stats.error_passive++;
> +			break;
> +		case CAN_STATE_BUS_OFF:
> +			cf->can_id |= CAN_ERR_BUSOFF;
> +			can_bus_off(sl->dev);
> +
> +			/* Disable status fetching until
> +			 * serial interface is restarted
> +			 */
> +			cancel_delayed_work_sync(&sl->work);
> +			break;
> +		case CAN_STATE_STOPPED:
> +		case CAN_STATE_SLEEPING:
> +		case CAN_STATE_MAX:
> +			WARN_ON_ONCE(1);
> +			break;
> +		}
> +	}
> +
> +	sl->can_flags = new_flags;
> +	sl->can.state = state;
> +
> +	netif_rx_ni(skb);
> +
> +	stats->rx_packets++;
> +	stats->rx_bytes += cf->can_dlc;

Please don't touch cf after netif_rx_ni();

> +
> +	return;
> +}
> +
>  /* Send one completely decapsulated can_frame to the network layer */
>  static void slc_bump(struct slcan *sl)
>  {
> @@ -190,6 +335,9 @@ static void slc_bump(struct slcan *sl)
>  	can_id = 0;
>  
>  	switch (*cmd) {
> +	case 'F':
> +		slc_handle_flags(sl);
> +		return;
>  	case 'r':
>  		can_id = CAN_RTR_FLAG;
>  		/* fallthrough */
> @@ -258,7 +406,7 @@ static void slcan_unesc(struct slcan *sl, unsigned char s)
>  {
>  	if ((s == '\r') || (s == '\a')) { /* CR or BEL ends the pdu */
>  		if (!test_and_clear_bit(SLF_ERROR, &sl->flags) &&
> -		    (sl->rcount > 4))  {
> +		    (sl->rcount > 2))  {
>  			slc_bump(sl);
>  		}
>  		sl->rcount = 0;
> @@ -454,6 +602,38 @@ static int slc_open(struct net_device *dev)
>  	return 0;
>  }
>  
> +static int slc_set_mode(struct net_device *dev, enum can_mode mode)
> +{
> +	struct slcan *sl = netdev_priv(dev);
> +	int err;
> +
> +	switch (mode) {
> +	case CAN_MODE_START:
> +		spin_lock_bh(&sl->lock);
> +
> +		err = slc_close_device(sl);
> +		if (err) {
> +			spin_unlock_bh(&sl->lock);
> +			return err;
> +		}
> +		err = slc_open_device(sl);
> +		if (err) {
> +			spin_unlock_bh(&sl->lock);
> +			return err;
> +		}
> +
> +		netif_wake_queue(dev);
> +
> +		spin_unlock_bh(&sl->lock);
> +		break;
> +
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
>  static int slc_set_bittiming(struct net_device *dev)
>  {
>  	struct slcan *sl = netdev_priv(dev);
> @@ -655,8 +835,12 @@ static int slcan_open(struct tty_struct *tty)
>  
>  	sl->tty = tty;
>  	tty->disc_data = sl;
> +	INIT_DELAYED_WORK(&sl->work, slcan_status_work);
>  
> +	/* slc_close_device expects the lock to be taken */
> +	spin_lock_bh(&sl->lock);
>  	err = slc_close_device(sl);
> +	spin_unlock_bh(&sl->lock);
>  	if (err)
>  		goto err_free_chan;
>  
> @@ -666,6 +850,7 @@ static int slcan_open(struct tty_struct *tty)
>  		sl->xleft    = 0;
>  
>  		sl->can.do_set_bittiming = slc_set_bittiming;
> +		sl->can.do_set_mode = slc_set_mode;
>  		set_bit(SLF_INUSE, &sl->flags);
>  
>  		err = register_candev(sl->dev);
> 

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 #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 242 bytes --]

  reply	other threads:[~2014-04-17 19:22 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-04-15 14:51 Bitrate and CAN status support for slcan Alexander Stein
2014-04-15 14:51 ` [PATCH 1/5] can: slcan: Fix spinlock variant Alexander Stein
2014-04-22 19:58   ` Oliver Hartkopp
2014-04-24 20:32   ` Marc Kleine-Budde
2014-04-15 14:51 ` [PATCH 2/5] can: slcan: Convert to can_dev interface Alexander Stein
2014-04-17 19:10   ` Marc Kleine-Budde
2014-04-15 14:51 ` [PATCH 3/5] can: slcan: Send open/close command upon interface up/down Alexander Stein
2014-04-15 14:51 ` [PATCH 4/5] can: slcan: Add bitrate change support Alexander Stein
2014-04-17 19:13   ` Marc Kleine-Budde
2014-04-15 14:51 ` [PATCH 5/5] can: slcan: Add CAN status flag support Alexander Stein
2014-04-17 19:22   ` Marc Kleine-Budde [this message]
2014-04-19 20:38   ` Oliver Hartkopp
2014-04-19 19:24 ` Bitrate and CAN status support for slcan Oliver Hartkopp
2014-04-22  7:08   ` Alexander Stein
2014-04-22 19:50     ` Oliver Hartkopp

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=535029DB.5010105@pengutronix.de \
    --to=mkl@pengutronix.de \
    --cc=alexander.stein@systec-electronic.com \
    --cc=linux-can@vger.kernel.org \
    --cc=wg@grandegger.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).