can: slcan: extend the protocol with error info
It extends the protocol to receive the adapter CAN communication errors and forward them to the netdev upper levels. Link: https://lore.kernel.org/all/20220628163137.413025-12-dario.binacchi@amarulasolutions.com Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com> Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
parent
4de0e8efa0
commit
b32ff46685
@ -175,7 +175,7 @@ int slcan_enable_err_rst_on_open(struct net_device *ndev, bool on)
|
||||
************************************************************************/
|
||||
|
||||
/* Send one completely decapsulated can_frame to the network layer */
|
||||
static void slc_bump(struct slcan *sl)
|
||||
static void slc_bump_frame(struct slcan *sl)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct can_frame *cf;
|
||||
@ -254,6 +254,144 @@ decode_failed:
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
/* An error frame can contain more than one type of error.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* e1a : len 1, errors: ACK error
|
||||
* e3bcO: len 3, errors: Bit0 error, CRC error, Tx overrun error
|
||||
*/
|
||||
static void slc_bump_err(struct slcan *sl)
|
||||
{
|
||||
struct net_device *dev = sl->dev;
|
||||
struct sk_buff *skb;
|
||||
struct can_frame *cf;
|
||||
char *cmd = sl->rbuff;
|
||||
bool rx_errors = false, tx_errors = false, rx_over_errors = false;
|
||||
int i, len;
|
||||
|
||||
/* get len from sanitized ASCII value */
|
||||
len = cmd[1];
|
||||
if (len >= '0' && len < '9')
|
||||
len -= '0';
|
||||
else
|
||||
return;
|
||||
|
||||
if ((len + SLC_CMD_LEN + 1) > sl->rcount)
|
||||
return;
|
||||
|
||||
skb = alloc_can_err_skb(dev, &cf);
|
||||
|
||||
if (skb)
|
||||
cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
|
||||
|
||||
cmd += SLC_CMD_LEN + 1;
|
||||
for (i = 0; i < len; i++, cmd++) {
|
||||
switch (*cmd) {
|
||||
case 'a':
|
||||
netdev_dbg(dev, "ACK error\n");
|
||||
tx_errors = true;
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_ACK;
|
||||
cf->data[3] = CAN_ERR_PROT_LOC_ACK;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'b':
|
||||
netdev_dbg(dev, "Bit0 error\n");
|
||||
tx_errors = true;
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_BIT0;
|
||||
|
||||
break;
|
||||
case 'B':
|
||||
netdev_dbg(dev, "Bit1 error\n");
|
||||
tx_errors = true;
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_BIT1;
|
||||
|
||||
break;
|
||||
case 'c':
|
||||
netdev_dbg(dev, "CRC error\n");
|
||||
rx_errors = true;
|
||||
if (skb) {
|
||||
cf->data[2] |= CAN_ERR_PROT_BIT;
|
||||
cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'f':
|
||||
netdev_dbg(dev, "Form Error\n");
|
||||
rx_errors = true;
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_FORM;
|
||||
|
||||
break;
|
||||
case 'o':
|
||||
netdev_dbg(dev, "Rx overrun error\n");
|
||||
rx_over_errors = true;
|
||||
rx_errors = true;
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'O':
|
||||
netdev_dbg(dev, "Tx overrun error\n");
|
||||
tx_errors = true;
|
||||
if (skb) {
|
||||
cf->can_id |= CAN_ERR_CRTL;
|
||||
cf->data[1] = CAN_ERR_CRTL_TX_OVERFLOW;
|
||||
}
|
||||
|
||||
break;
|
||||
case 's':
|
||||
netdev_dbg(dev, "Stuff error\n");
|
||||
rx_errors = true;
|
||||
if (skb)
|
||||
cf->data[2] |= CAN_ERR_PROT_STUFF;
|
||||
|
||||
break;
|
||||
default:
|
||||
if (skb)
|
||||
dev_kfree_skb(skb);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rx_errors)
|
||||
dev->stats.rx_errors++;
|
||||
|
||||
if (rx_over_errors)
|
||||
dev->stats.rx_over_errors++;
|
||||
|
||||
if (tx_errors)
|
||||
dev->stats.tx_errors++;
|
||||
|
||||
if (skb)
|
||||
netif_rx(skb);
|
||||
}
|
||||
|
||||
static void slc_bump(struct slcan *sl)
|
||||
{
|
||||
switch (sl->rbuff[0]) {
|
||||
case 'r':
|
||||
fallthrough;
|
||||
case 't':
|
||||
fallthrough;
|
||||
case 'R':
|
||||
fallthrough;
|
||||
case 'T':
|
||||
return slc_bump_frame(sl);
|
||||
case 'e':
|
||||
return slc_bump_err(sl);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* parse tty input stream */
|
||||
static void slcan_unesc(struct slcan *sl, unsigned char s)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user