[RFC,v2,13/13] can: slcan: extend the protocol with CAN state info
Commit Message
It extends the protocol to receive the adapter CAN state changes
(warning, busoff, etc.) and forward them to the netdev upper levels.
Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---
(no changes since v1)
drivers/net/can/slcan/slcan-core.c | 65 ++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
@@ -78,6 +78,9 @@ MODULE_PARM_DESC(maxdev, "Maximum number of slcan interfaces");
#define SLC_CMD_LEN 1
#define SLC_SFF_ID_LEN 3
#define SLC_EFF_ID_LEN 8
+#define SLC_STATE_LEN 1
+#define SLC_STATE_BE_RXCNT_LEN 3
+#define SLC_STATE_BE_TXCNT_LEN 3
struct slcan {
struct can_priv can;
@@ -182,6 +185,66 @@ int slcan_enable_err_rst_on_open(struct net_device *ndev, bool on)
* STANDARD SLCAN DECAPSULATION *
************************************************************************/
+static void slc_bump_state(struct slcan *sl)
+{
+ struct net_device *dev = sl->dev;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+ char *cmd = sl->rbuff;
+ u32 rxerr, txerr;
+ enum can_state state, rx_state, tx_state;
+
+ if (*cmd != 's')
+ return;
+
+ cmd += SLC_CMD_LEN;
+ switch (*cmd) {
+ case 'a':
+ state = CAN_STATE_ERROR_ACTIVE;
+ break;
+ case 'w':
+ state = CAN_STATE_ERROR_WARNING;
+ break;
+ case 'p':
+ state = CAN_STATE_ERROR_PASSIVE;
+ break;
+ case 'f':
+ state = CAN_STATE_BUS_OFF;
+ break;
+ default:
+ return;
+ }
+
+ if (state == sl->can.state)
+ return;
+
+ cmd += SLC_STATE_BE_RXCNT_LEN + 1;
+ cmd[SLC_STATE_BE_TXCNT_LEN] = 0;
+ if (kstrtou32(cmd, 10, &txerr))
+ return;
+
+ *cmd = 0;
+ cmd -= SLC_STATE_BE_RXCNT_LEN;
+ if (kstrtou32(cmd, 10, &rxerr))
+ return;
+
+ skb = alloc_can_err_skb(dev, &cf);
+ if (unlikely(!skb))
+ return;
+
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+
+ tx_state = txerr >= rxerr ? state : 0;
+ rx_state = txerr <= rxerr ? state : 0;
+ can_change_state(dev, cf, tx_state, rx_state);
+
+ if (state == CAN_STATE_BUS_OFF)
+ can_bus_off(dev);
+
+ netif_rx(skb);
+}
+
static void slc_bump_err(struct slcan *sl)
{
struct net_device *dev = sl->dev;
@@ -354,6 +417,8 @@ static void slc_bump(struct slcan *sl)
return slc_bump_frame(sl);
case 'e':
return slc_bump_err(sl);
+ case 's':
+ return slc_bump_state(sl);
default:
return;
}